blob: 9265775065ab795a50ef744b2750899045690fce [file] [log] [blame]
// Copyright 2022, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Project Rialto main source file.
#![no_main]
#![no_std]
mod communication;
mod error;
mod exceptions;
mod fdt;
extern crate alloc;
use crate::communication::VsockStream;
use crate::error::{Error, Result};
use crate::fdt::{read_dice_range_from, read_is_strict_boot, read_vendor_hashtree_root_digest};
use alloc::boxed::Box;
use ciborium_io::Write;
use core::num::NonZeroUsize;
use core::slice;
use diced_open_dice::{bcc_handover_parse, DiceArtifacts};
use fdtpci::PciInfo;
use libfdt::FdtError;
use log::{debug, error, info};
use service_vm_comm::{ServiceVmRequest, VmType};
use service_vm_fake_chain::service_vm;
use service_vm_requests::{process_request, RequestContext};
use virtio_drivers::{
device::socket::{VsockAddr, VMADDR_CID_HOST},
transport::{pci::bus::PciRoot, DeviceType, Transport},
Hal,
};
use vmbase::{
configure_heap,
fdt::SwiotlbInfo,
generate_image_header,
hyp::{get_mem_sharer, get_mmio_guard},
layout::{self, crosvm, UART_PAGE_ADDR},
main,
memory::{MemoryTracker, PageTable, MEMORY, PAGE_SIZE, SIZE_128KB},
power::reboot,
virtio::{
pci::{self, PciTransportIterator, VirtIOSocket},
HalImpl,
},
};
fn host_addr(fdt: &libfdt::Fdt) -> Result<VsockAddr> {
Ok(VsockAddr { cid: VMADDR_CID_HOST, port: vm_type(fdt)?.port() })
}
fn vm_type(fdt: &libfdt::Fdt) -> Result<VmType> {
if read_is_strict_boot(fdt)? {
Ok(VmType::ProtectedVm)
} else {
Ok(VmType::NonProtectedVm)
}
}
fn new_page_table() -> Result<PageTable> {
let mut page_table = PageTable::default();
page_table.map_data(&layout::scratch_range().into())?;
page_table.map_data(&layout::stack_range(40 * PAGE_SIZE).into())?;
page_table.map_code(&layout::text_range().into())?;
page_table.map_rodata(&layout::rodata_range().into())?;
page_table.map_device(&layout::console_uart_page().into())?;
Ok(page_table)
}
/// # Safety
///
/// Behavior is undefined if any of the following conditions are violated:
/// * The `fdt_addr` must be a valid pointer and points to a valid `Fdt`.
unsafe fn try_main(fdt_addr: usize) -> Result<()> {
info!("Welcome to Rialto!");
let page_table = new_page_table()?;
MEMORY.lock().replace(MemoryTracker::new(
page_table,
crosvm::MEM_START..layout::MAX_VIRT_ADDR,
crosvm::MMIO_RANGE,
None, // Rialto doesn't have any payload for now.
));
let fdt_range = MEMORY
.lock()
.as_mut()
.unwrap()
.alloc(fdt_addr, NonZeroUsize::new(crosvm::FDT_MAX_SIZE).unwrap())?;
// SAFETY: The tracker validated the range to be in main memory, mapped, and not overlap.
let fdt = unsafe { slice::from_raw_parts(fdt_range.start as *mut u8, fdt_range.len()) };
// We do not need to validate the DT since it is already validated in pvmfw.
let fdt = libfdt::Fdt::from_slice(fdt)?;
let memory_range = fdt.first_memory_range()?;
MEMORY.lock().as_mut().unwrap().shrink(&memory_range).inspect_err(|_| {
error!("Failed to use memory range value from DT: {memory_range:#x?}");
})?;
if let Some(mem_sharer) = get_mem_sharer() {
let granule = mem_sharer.granule()?;
MEMORY.lock().as_mut().unwrap().init_dynamic_shared_pool(granule).inspect_err(|_| {
error!("Failed to initialize dynamically shared pool.");
})?;
} else if let Ok(swiotlb_info) = SwiotlbInfo::new_from_fdt(fdt) {
let range = swiotlb_info.fixed_range().ok_or_else(|| {
error!("Pre-shared pool range not specified in swiotlb node");
Error::from(FdtError::BadValue)
})?;
MEMORY.lock().as_mut().unwrap().init_static_shared_pool(range).inspect_err(|_| {
error!("Failed to initialize pre-shared pool.");
})?;
} else {
info!("No MEM_SHARE capability detected or swiotlb found: allocating buffers from heap.");
MEMORY.lock().as_mut().unwrap().init_heap_shared_pool().inspect_err(|_| {
error!("Failed to initialize heap-based pseudo-shared pool.");
})?;
}
let bcc_handover: Box<dyn DiceArtifacts> = match vm_type(fdt)? {
VmType::ProtectedVm => {
let dice_range = read_dice_range_from(fdt)?;
info!("DICE range: {dice_range:#x?}");
// SAFETY: This region was written by pvmfw in its writable_data region. The region
// has no overlap with the main memory region and is safe to be mapped as read-only
// data.
let res = unsafe {
MEMORY.lock().as_mut().unwrap().alloc_range_outside_main_memory(&dice_range)
};
res.inspect_err(|_| {
error!("Failed to use DICE range from DT: {dice_range:#x?}");
})?;
let dice_start = dice_range.start as *const u8;
// SAFETY: There's no memory overlap and the region is mapped as read-only data.
let bcc_handover = unsafe { slice::from_raw_parts(dice_start, dice_range.len()) };
Box::new(bcc_handover_parse(bcc_handover)?)
}
// Currently, a sample DICE data is used for non-protected VMs, as these VMs only run
// in tests at the moment.
VmType::NonProtectedVm => Box::new(service_vm::fake_service_vm_dice_artifacts()?),
};
let pci_info = PciInfo::from_fdt(fdt)?;
debug!("PCI: {pci_info:#x?}");
let mut pci_root = pci::initialize(pci_info, MEMORY.lock().as_mut().unwrap())
.map_err(Error::PciInitializationFailed)?;
debug!("PCI root: {pci_root:#x?}");
let socket_device = find_socket_device::<HalImpl>(&mut pci_root)?;
debug!("Found socket device: guest cid = {:?}", socket_device.guest_cid());
let vendor_hashtree_root_digest = read_vendor_hashtree_root_digest(fdt)?;
let request_context =
RequestContext { dice_artifacts: bcc_handover.as_ref(), vendor_hashtree_root_digest };
let mut vsock_stream = VsockStream::new(socket_device, host_addr(fdt)?)?;
while let ServiceVmRequest::Process(req) = vsock_stream.read_request()? {
info!("Received request: {}", req.name());
let response = process_request(req, &request_context);
info!("Sending response: {}", response.name());
vsock_stream.write_response(&response)?;
vsock_stream.flush()?;
}
vsock_stream.shutdown()?;
Ok(())
}
fn find_socket_device<T: Hal>(pci_root: &mut PciRoot) -> Result<VirtIOSocket<T>> {
PciTransportIterator::<T>::new(pci_root)
.find(|t| DeviceType::Socket == t.device_type())
.map(VirtIOSocket::<T>::new)
.transpose()
.map_err(Error::VirtIOSocketCreationFailed)?
.ok_or(Error::MissingVirtIOSocketDevice)
}
fn try_unshare_all_memory() -> Result<()> {
info!("Starting unsharing memory...");
// No logging after unmapping UART.
if let Some(mmio_guard) = get_mmio_guard() {
mmio_guard.unmap(UART_PAGE_ADDR)?;
}
// Unshares all memory and deactivates page table.
drop(MEMORY.lock().take());
Ok(())
}
fn unshare_all_memory() {
if let Err(e) = try_unshare_all_memory() {
error!("Failed to unshare the memory: {e}");
}
}
/// Entry point for Rialto.
pub fn main(fdt_addr: u64, _a1: u64, _a2: u64, _a3: u64) {
log::set_max_level(log::LevelFilter::Debug);
// SAFETY: `fdt_addr` is supposed to be a valid pointer and points to
// a valid `Fdt`.
match unsafe { try_main(fdt_addr as usize) } {
Ok(()) => unshare_all_memory(),
Err(e) => {
error!("Rialto failed with {e}");
unshare_all_memory();
reboot()
}
}
}
/// Flushes data caches over the provided address range.
///
/// # Safety
///
/// The provided address and size must be to an address range that is valid for read and write
/// (typically on the stack, .bss, .data, or provided BCC) from a single allocation
/// (e.g. stack array).
#[no_mangle]
unsafe extern "C" fn DiceClearMemory(
_ctx: *mut core::ffi::c_void,
size: usize,
addr: *mut core::ffi::c_void,
) {
use core::slice;
use vmbase::memory::flushed_zeroize;
// SAFETY: We require our caller to provide a valid range within a single object. The open-dice
// always calls this on individual stack-allocated arrays which ensures that.
let region = unsafe { slice::from_raw_parts_mut(addr as *mut u8, size) };
flushed_zeroize(region)
}
generate_image_header!();
main!(main);
configure_heap!(SIZE_128KB * 2);