platform: generic-x86_64: Add support for crosvm pci bus with virtio using rust acpi crate
TODO: Call PCI init function
Bug: 304585390
Change-Id: Id9ecc3ad39bce722a4cbaef92d9e501d06e45933
diff --git a/platform/generic-x86_64/rules.mk b/platform/generic-x86_64/rules.mk
index 2de00d2..c32ef81 100644
--- a/platform/generic-x86_64/rules.mk
+++ b/platform/generic-x86_64/rules.mk
@@ -27,6 +27,7 @@
$(LOCAL_DIR)/platform.c \
MODULE_DEPS += \
+ $(LOCAL_DIR)/rust \
dev/interrupt/x86_lapic \
dev/timer/x86_generic \
dev/virtio/vsock-rust \
diff --git a/platform/generic-x86_64/rust/rules.mk b/platform/generic-x86_64/rust/rules.mk
new file mode 100644
index 0000000..8238d1d
--- /dev/null
+++ b/platform/generic-x86_64/rust/rules.mk
@@ -0,0 +1,15 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_CRATE_NAME := platform_generic_x86_64
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/src/lib.rs \
+
+MODULE_LIBRARY_DEPS += \
+ external/lk/lib/rust_support \
+ external/rust/crates/acpi \
+ external/rust/crates/log \
+
+include make/library.mk
diff --git a/platform/generic-x86_64/rust/src/lib.rs b/platform/generic-x86_64/rust/src/lib.rs
new file mode 100644
index 0000000..53b71c1
--- /dev/null
+++ b/platform/generic-x86_64/rust/src/lib.rs
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2024 Google Inc. All rights reserved
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#![no_std]
+#![feature(c_str_literals)]
+
+use acpi::{AcpiHandler, AcpiTables, PhysicalMapping};
+
+use rust_support::{
+ init::lk_init_level,
+ mmu::{ARCH_MMU_FLAG_CACHED, ARCH_MMU_FLAG_PERM_NO_EXECUTE, ARCH_MMU_FLAG_PERM_RO, PAGE_SIZE},
+ vmm::{vmm_alloc_physical, vmm_free_region, vmm_get_kernel_aspace},
+ LK_INIT_HOOK,
+};
+
+use core::ffi::{c_uint, c_void};
+use core::ptr::NonNull;
+
+const PAGE_MASK: usize = PAGE_SIZE as usize - 1;
+
+#[derive(Clone)]
+struct LkAcpiHandler;
+
+impl AcpiHandler for LkAcpiHandler {
+ // SAFETY: map_physical_region get passed a valid physical address range
+ // if the assumptions below are met. It returns a read-only mapping to
+ // that region and the caller must not create any mutable references
+ // from the returned pointe.
+ unsafe fn map_physical_region<T>(
+ &self,
+ physical_address: usize,
+ size: usize,
+ ) -> PhysicalMapping<Self, T> {
+ let page_paddr = physical_address & !PAGE_MASK;
+ let offset = physical_address - page_paddr;
+ let aligned_size = (size + offset + PAGE_MASK) & !PAGE_MASK;
+ let mut ptr: *mut c_void = core::ptr::null_mut();
+ let ret = vmm_alloc_physical(
+ vmm_get_kernel_aspace(),
+ c"rust-acpi".as_ptr() as _,
+ aligned_size,
+ &mut ptr,
+ 0,
+ page_paddr,
+ 0,
+ ARCH_MMU_FLAG_CACHED | ARCH_MMU_FLAG_PERM_RO | ARCH_MMU_FLAG_PERM_NO_EXECUTE,
+ );
+
+ // If vmm_alloc_physical failed, panic.
+ // Ideally we should return an error to the caller in that case, but
+ // the api defined by the acpi crate does not allow that.
+ if ret != 0 {
+ panic!("vmm_alloc_physical failed, but map_physical_region is not allowed to return an error")
+ }
+
+ let nonnullptr = NonNull::new(ptr.wrapping_add(offset) as _).unwrap();
+
+ PhysicalMapping::new(physical_address, nonnullptr, size, aligned_size - offset, Self)
+ }
+ fn unmap_physical_region<T>(region: &PhysicalMapping<Self, T>) {
+ let ptr = region.virtual_start().as_ptr() as _;
+ // SAFETY:: ptr came from vmm_alloc_physical
+ unsafe { vmm_free_region(vmm_get_kernel_aspace(), ptr) };
+ }
+}
+
+extern "C" fn platform_acpi_init_func(_level: c_uint) {
+ // SAFETY: search_for_rsdp_bios searches for a RSDP on BIOS systems.
+ // It is not safe to call on a UEFI system. crosvm currently emulates
+ // a BIOS system.
+ let acpi_tables = match unsafe { AcpiTables::search_for_rsdp_bios(LkAcpiHandler) } {
+ Ok(acpi_tables) => acpi_tables,
+ Err(error) => {
+ log::error!("search_for_rsdp_bios failed: {error:?}");
+ return;
+ }
+ };
+ let mcfg_table = match acpi_tables.find_table::<acpi::mcfg::Mcfg>() {
+ Ok(mcfg_table) => mcfg_table,
+ Err(error) => {
+ log::error!("filed to find mcfg_table: {error:?}");
+ return;
+ }
+ };
+ let mcfg_entries = mcfg_table.entries();
+ let entry = mcfg_entries[0];
+
+ let entry_size = (1 + entry.bus_number_end as usize - entry.bus_number_start as usize) << 20;
+
+ log::error!(
+ "TODO: call init function for pci bus at {:#x}, size {entry_size:#x}",
+ entry.base_address as usize
+ );
+}
+
+LK_INIT_HOOK!(platform_acpi_init, platform_acpi_init_func, lk_init_level::LK_INIT_LEVEL_THREADING);