1 /*
2 * Copyright (c) 2024 Google Inc. All rights reserved
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #![no_std]
25 #![feature(cfg_version)]
26 // C string literals were stabilized in Rust 1.77
27 #![cfg_attr(not(version("1.77")), feature(c_str_literals))]
28
29 use acpi::{AcpiHandler, AcpiTables, PhysicalMapping};
30
31 use rust_support::{
32 init::lk_init_level,
33 mmu::{ARCH_MMU_FLAG_CACHED, ARCH_MMU_FLAG_PERM_NO_EXECUTE, ARCH_MMU_FLAG_PERM_RO, PAGE_SIZE},
34 vmm::{vmm_alloc_physical, vmm_free_region, vmm_get_kernel_aspace},
35 LK_INIT_HOOK,
36 };
37
38 use core::ffi::{c_uint, c_void};
39 use core::ptr::NonNull;
40
41 const PAGE_MASK: usize = PAGE_SIZE as usize - 1;
42
43 #[derive(Clone)]
44 struct LkAcpiHandler;
45
46 impl AcpiHandler for LkAcpiHandler {
47 // SAFETY: map_physical_region get passed a valid physical address range
48 // if the assumptions below are met. It returns a read-only mapping to
49 // that region and the caller must not create any mutable references
50 // from the returned pointe.
map_physical_region<T>( &self, physical_address: usize, size: usize, ) -> PhysicalMapping<Self, T>51 unsafe fn map_physical_region<T>(
52 &self,
53 physical_address: usize,
54 size: usize,
55 ) -> PhysicalMapping<Self, T> {
56 let page_paddr = physical_address & !PAGE_MASK;
57 let offset = physical_address - page_paddr;
58 let aligned_size = (size + offset + PAGE_MASK) & !PAGE_MASK;
59 let mut ptr: *mut c_void = core::ptr::null_mut();
60 let ret = vmm_alloc_physical(
61 vmm_get_kernel_aspace(),
62 c"rust-acpi".as_ptr() as _,
63 aligned_size,
64 &mut ptr,
65 0,
66 page_paddr,
67 0,
68 ARCH_MMU_FLAG_CACHED | ARCH_MMU_FLAG_PERM_RO | ARCH_MMU_FLAG_PERM_NO_EXECUTE,
69 );
70
71 // If vmm_alloc_physical failed, panic.
72 // Ideally we should return an error to the caller in that case, but
73 // the api defined by the acpi crate does not allow that.
74 if ret != 0 {
75 panic!("vmm_alloc_physical failed, but map_physical_region is not allowed to return an error")
76 }
77
78 let nonnullptr = NonNull::new(ptr.wrapping_add(offset) as _).unwrap();
79
80 PhysicalMapping::new(physical_address, nonnullptr, size, aligned_size - offset, Self)
81 }
unmap_physical_region<T>(region: &PhysicalMapping<Self, T>)82 fn unmap_physical_region<T>(region: &PhysicalMapping<Self, T>) {
83 let ptr = region.virtual_start().as_ptr() as _;
84 // SAFETY:: ptr came from vmm_alloc_physical
85 unsafe { vmm_free_region(vmm_get_kernel_aspace(), ptr) };
86 }
87 }
88
89 /// Search the ACPI tables for the physical address which is the base of the MMIO
90 /// region and pass it to `pci_init_mmio` to initialize the virtio-vsock bridge.
91 ///
92 /// # Safety
93 ///
94 /// A caller must ensure that this function is called exactly once during kernel
95 /// initialization on a system which has a BIOS (as opposed to UEFI systems).
platform_acpi_init_func(_level: c_uint)96 unsafe extern "C" fn platform_acpi_init_func(_level: c_uint) {
97 // SAFETY: search_for_rsdp_bios searches for a RSDP on BIOS systems.
98 // It is not safe to call on a UEFI system. crosvm currently emulates
99 // a BIOS system.
100 let acpi_tables = match unsafe { AcpiTables::search_for_rsdp_bios(LkAcpiHandler) } {
101 Ok(acpi_tables) => acpi_tables,
102 Err(error) => {
103 log::error!("search_for_rsdp_bios failed: {error:?}");
104 return;
105 }
106 };
107 let mcfg_table = match acpi_tables.find_table::<acpi::mcfg::Mcfg>() {
108 Ok(mcfg_table) => mcfg_table,
109 Err(error) => {
110 log::error!("filed to find mcfg_table: {error:?}");
111 return;
112 }
113 };
114 let mcfg_entries = mcfg_table.entries();
115 let entry = mcfg_entries[0];
116
117 let entry_size = (1 + entry.bus_number_end as usize - entry.bus_number_start as usize) << 20;
118
119 // SAFETY: the first argument must be a valid physical address pointing to the base of the MMIO region.
120 // The second argument must be the size of the MMIO region and be valid under the PCI express version
121 // implemented by the system. `pci_init_mmio` must not have been called previously.
122 unsafe { vsock::pci_init_mmio(entry.base_address as usize, entry_size, 1usize << 15) };
123 }
124
125 LK_INIT_HOOK!(platform_acpi_init, platform_acpi_init_func, lk_init_level::LK_INIT_LEVEL_THREADING);
126