blob: f49d79ba85c4d6c4eb84eaf174b97c6bca382f34 [file] [log] [blame]
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +00001// Copyright 2022, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Low-level allocation and tracking of main memory.
16
Pierre-Clément Tosi462bdf42024-10-30 17:46:23 +000017use crate::entry::RebootReason;
18use crate::fdt;
Alice Wang4be4dd02023-06-07 07:50:40 +000019use crate::helpers::PVMFW_PAGE_SIZE;
Alice Wanga3931aa2023-07-05 12:52:09 +000020use aarch64_paging::paging::VirtualAddress;
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +000021use aarch64_paging::MapError;
Pierre-Clément Tosi462bdf42024-10-30 17:46:23 +000022use core::num::NonZeroUsize;
Alice Wanga3931aa2023-07-05 12:52:09 +000023use core::ops::Range;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000024use core::result;
Pierre-Clément Tosi462bdf42024-10-30 17:46:23 +000025use core::slice;
26use log::debug;
Alice Wang93ee98a2023-06-08 08:20:39 +000027use log::error;
Pierre-Clément Tosi462bdf42024-10-30 17:46:23 +000028use log::info;
29use log::warn;
Pierre-Clément Tosi3d4c5c32023-05-31 16:57:06 +000030use vmbase::{
Pierre-Clément Tosi462bdf42024-10-30 17:46:23 +000031 hyp::get_mem_sharer,
32 layout::{self, crosvm},
33 memory::{PageTable, MEMORY, SIZE_2MB, SIZE_4KB},
Alice Wang93ee98a2023-06-08 08:20:39 +000034 util::align_up,
Pierre-Clément Tosi3d4c5c32023-05-31 16:57:06 +000035};
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000036
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +000037/// Returns memory range reserved for the appended payload.
Alice Wanga3931aa2023-07-05 12:52:09 +000038pub fn appended_payload_range() -> Range<VirtualAddress> {
Alice Wang8b097042023-07-06 14:56:58 +000039 let start = align_up(layout::binary_end().0, SIZE_4KB).unwrap();
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +000040 // pvmfw is contained in a 2MiB region so the payload can't be larger than the 2MiB alignment.
Alice Wangeacb7382023-06-05 12:53:54 +000041 let end = align_up(start, SIZE_2MB).unwrap();
Alice Wanga3931aa2023-07-05 12:52:09 +000042 VirtualAddress(start)..VirtualAddress(end)
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +000043}
44
45/// Region allocated for the stack.
Alice Wanga3931aa2023-07-05 12:52:09 +000046pub fn stack_range() -> Range<VirtualAddress> {
Pierre-Clément Tosi9ddfab52024-04-24 23:03:46 +010047 const STACK_PAGES: usize = 12;
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +000048
49 layout::stack_range(STACK_PAGES * PVMFW_PAGE_SIZE)
50}
51
52pub fn init_page_table() -> result::Result<PageTable, MapError> {
Alice Wangee5b1802023-06-07 07:41:54 +000053 let mut page_table = PageTable::default();
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +000054
55 // Stack and scratch ranges are explicitly zeroed and flushed before jumping to payload,
56 // so dirty state management can be omitted.
Alice Wanga3931aa2023-07-05 12:52:09 +000057 page_table.map_data(&layout::scratch_range().into())?;
58 page_table.map_data(&stack_range().into())?;
59 page_table.map_code(&layout::text_range().into())?;
60 page_table.map_rodata(&layout::rodata_range().into())?;
61 page_table.map_data_dbm(&appended_payload_range().into())?;
Pierre-Clément Tosi38a36212024-06-06 11:30:39 +010062 if let Err(e) = page_table.map_device(&layout::console_uart_page().into()) {
Alice Wang807fa592023-06-02 09:54:43 +000063 error!("Failed to remap the UART as a dynamic page table entry: {e}");
64 return Err(e);
65 }
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +000066 Ok(page_table)
67}
Pierre-Clément Tosi462bdf42024-10-30 17:46:23 +000068
69pub(crate) struct MemorySlices<'a> {
70 pub fdt: &'a mut libfdt::Fdt,
71 pub kernel: &'a [u8],
72 pub ramdisk: Option<&'a [u8]>,
73}
74
75impl<'a> MemorySlices<'a> {
76 pub fn new(
77 fdt: usize,
78 kernel: usize,
79 kernel_size: usize,
80 vm_dtbo: Option<&mut [u8]>,
81 vm_ref_dt: Option<&[u8]>,
82 ) -> Result<Self, RebootReason> {
83 let fdt_size = NonZeroUsize::new(crosvm::FDT_MAX_SIZE).unwrap();
84 // TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
85 // e.g. by generating a DTBO for a template DT in main() and, on return, re-map DT as RW,
86 // overwrite with the template DT and apply the DTBO.
87 let range = MEMORY.lock().as_mut().unwrap().alloc_mut(fdt, fdt_size).map_err(|e| {
88 error!("Failed to allocate the FDT range: {e}");
89 RebootReason::InternalError
90 })?;
91
92 // SAFETY: The tracker validated the range to be in main memory, mapped, and not overlap.
93 let fdt = unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) };
94
95 let info = fdt::sanitize_device_tree(fdt, vm_dtbo, vm_ref_dt)?;
96 let fdt = libfdt::Fdt::from_mut_slice(fdt).map_err(|e| {
97 error!("Failed to load sanitized FDT: {e}");
98 RebootReason::InvalidFdt
99 })?;
100 debug!("Fdt passed validation!");
101
102 let memory_range = info.memory_range;
103 debug!("Resizing MemoryTracker to range {memory_range:#x?}");
104 MEMORY.lock().as_mut().unwrap().shrink(&memory_range).map_err(|e| {
105 error!("Failed to use memory range value from DT: {memory_range:#x?}: {e}");
106 RebootReason::InvalidFdt
107 })?;
108
109 if let Some(mem_sharer) = get_mem_sharer() {
110 let granule = mem_sharer.granule().map_err(|e| {
111 error!("Failed to get memory protection granule: {e}");
112 RebootReason::InternalError
113 })?;
114 MEMORY.lock().as_mut().unwrap().init_dynamic_shared_pool(granule).map_err(|e| {
115 error!("Failed to initialize dynamically shared pool: {e}");
116 RebootReason::InternalError
117 })?;
118 } else {
119 let range = info.swiotlb_info.fixed_range().ok_or_else(|| {
120 error!("Pre-shared pool range not specified in swiotlb node");
121 RebootReason::InvalidFdt
122 })?;
123
124 MEMORY.lock().as_mut().unwrap().init_static_shared_pool(range).map_err(|e| {
125 error!("Failed to initialize pre-shared pool {e}");
126 RebootReason::InvalidFdt
127 })?;
128 }
129
130 let kernel_range = if let Some(r) = info.kernel_range {
131 MEMORY.lock().as_mut().unwrap().alloc_range(&r).map_err(|e| {
132 error!("Failed to obtain the kernel range with DT range: {e}");
133 RebootReason::InternalError
134 })?
135 } else if cfg!(feature = "legacy") {
136 warn!("Failed to find the kernel range in the DT; falling back to legacy ABI");
137
138 let kernel_size = NonZeroUsize::new(kernel_size).ok_or_else(|| {
139 error!("Invalid kernel size: {kernel_size:#x}");
140 RebootReason::InvalidPayload
141 })?;
142
143 MEMORY.lock().as_mut().unwrap().alloc(kernel, kernel_size).map_err(|e| {
144 error!("Failed to obtain the kernel range with legacy range: {e}");
145 RebootReason::InternalError
146 })?
147 } else {
148 error!("Failed to locate the kernel from the DT");
149 return Err(RebootReason::InvalidPayload);
150 };
151
152 let kernel = kernel_range.start as *const u8;
153 // SAFETY: The tracker validated the range to be in main memory, mapped, and not overlap.
154 let kernel = unsafe { slice::from_raw_parts(kernel, kernel_range.len()) };
155
156 let ramdisk = if let Some(r) = info.initrd_range {
157 debug!("Located ramdisk at {r:?}");
158 let r = MEMORY.lock().as_mut().unwrap().alloc_range(&r).map_err(|e| {
159 error!("Failed to obtain the initrd range: {e}");
160 RebootReason::InvalidRamdisk
161 })?;
162
163 // SAFETY: The region was validated by memory to be in main memory, mapped, and
164 // not overlap.
165 Some(unsafe { slice::from_raw_parts(r.start as *const u8, r.len()) })
166 } else {
167 info!("Couldn't locate the ramdisk from the device tree");
168 None
169 };
170
171 Ok(Self { fdt, kernel, ramdisk })
172 }
173}