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 
platform_acpi_init_func(_level: c_uint)89 extern "C" fn platform_acpi_init_func(_level: c_uint) {
90     // SAFETY: search_for_rsdp_bios searches for a RSDP on BIOS systems.
91     // It is not safe to call on a UEFI system. crosvm currently emulates
92     // a BIOS system.
93     let acpi_tables = match unsafe { AcpiTables::search_for_rsdp_bios(LkAcpiHandler) } {
94         Ok(acpi_tables) => acpi_tables,
95         Err(error) => {
96             log::error!("search_for_rsdp_bios failed: {error:?}");
97             return;
98         }
99     };
100     let mcfg_table = match acpi_tables.find_table::<acpi::mcfg::Mcfg>() {
101         Ok(mcfg_table) => mcfg_table,
102         Err(error) => {
103             log::error!("filed to find mcfg_table: {error:?}");
104             return;
105         }
106     };
107     let mcfg_entries = mcfg_table.entries();
108     let entry = mcfg_entries[0];
109 
110     let entry_size = (1 + entry.bus_number_end as usize - entry.bus_number_start as usize) << 20;
111 
112     log::error!(
113         "TODO: call init function for pci bus at {:#x}, size {entry_size:#x}",
114         entry.base_address as usize
115     );
116 }
117 
118 LK_INIT_HOOK!(platform_acpi_init, platform_acpi_init_func, lk_init_level::LK_INIT_LEVEL_THREADING);
119