1 // 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 
17 use crate::entry::RebootReason;
18 use crate::fdt::{read_initrd_range_from, read_kernel_range_from};
19 use core::num::NonZeroUsize;
20 use core::slice;
21 use log::debug;
22 use log::error;
23 use log::info;
24 use log::warn;
25 use vmbase::{
26     layout::crosvm,
27     memory::{map_data, map_rodata, resize_available_memory},
28 };
29 
30 pub(crate) struct MemorySlices<'a> {
31     pub fdt: &'a mut libfdt::Fdt,
32     pub kernel: &'a [u8],
33     pub ramdisk: Option<&'a [u8]>,
34 }
35 
36 impl<'a> MemorySlices<'a> {
new(fdt: usize, kernel: usize, kernel_size: usize) -> Result<Self, RebootReason>37     pub fn new(fdt: usize, kernel: usize, kernel_size: usize) -> Result<Self, RebootReason> {
38         let fdt_size = NonZeroUsize::new(crosvm::FDT_MAX_SIZE).unwrap();
39         // TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
40         // e.g. by generating a DTBO for a template DT in main() and, on return, re-map DT as RW,
41         // overwrite with the template DT and apply the DTBO.
42         map_data(fdt, fdt_size).map_err(|e| {
43             error!("Failed to allocate the FDT range: {e}");
44             RebootReason::InternalError
45         })?;
46 
47         // SAFETY: map_data validated the range to be in main memory, mapped, and not overlap.
48         let untrusted_fdt = unsafe { slice::from_raw_parts_mut(fdt as *mut u8, fdt_size.into()) };
49         let untrusted_fdt = libfdt::Fdt::from_mut_slice(untrusted_fdt).map_err(|e| {
50             error!("Failed to load input FDT: {e}");
51             RebootReason::InvalidFdt
52         })?;
53 
54         let memory_range = untrusted_fdt.first_memory_range().map_err(|e| {
55             error!("Failed to read memory range from DT: {e}");
56             RebootReason::InvalidFdt
57         })?;
58         debug!("Resizing MemoryTracker to range {memory_range:#x?}");
59         resize_available_memory(&memory_range).map_err(|e| {
60             error!("Failed to use memory range value from DT: {memory_range:#x?}: {e}");
61             RebootReason::InvalidFdt
62         })?;
63 
64         let kernel_range = read_kernel_range_from(untrusted_fdt).map_err(|e| {
65             error!("Failed to read kernel range: {e}");
66             RebootReason::InvalidFdt
67         })?;
68         let (kernel_start, kernel_size) = if let Some(r) = kernel_range {
69             (r.start, r.len())
70         } else if cfg!(feature = "legacy") {
71             warn!("Failed to find the kernel range in the DT; falling back to legacy ABI");
72             (kernel, kernel_size)
73         } else {
74             error!("Failed to locate the kernel from the DT");
75             return Err(RebootReason::InvalidPayload);
76         };
77         let kernel_size = kernel_size.try_into().map_err(|_| {
78             error!("Invalid kernel size: {kernel_size:#x}");
79             RebootReason::InvalidPayload
80         })?;
81 
82         map_rodata(kernel_start, kernel_size).map_err(|e| {
83             error!("Failed to map kernel range: {e}");
84             RebootReason::InternalError
85         })?;
86 
87         let kernel = kernel_start as *const u8;
88         // SAFETY: map_rodata validated the range to be in main memory, mapped, and not overlap.
89         let kernel = unsafe { slice::from_raw_parts(kernel, kernel_size.into()) };
90 
91         let initrd_range = read_initrd_range_from(untrusted_fdt).map_err(|e| {
92             error!("Failed to read initrd range: {e}");
93             RebootReason::InvalidFdt
94         })?;
95         let ramdisk = if let Some(r) = initrd_range {
96             debug!("Located ramdisk at {r:?}");
97             let ramdisk_size = r.len().try_into().map_err(|_| {
98                 error!("Invalid ramdisk size: {:#x}", r.len());
99                 RebootReason::InvalidRamdisk
100             })?;
101             map_rodata(r.start, ramdisk_size).map_err(|e| {
102                 error!("Failed to obtain the initrd range: {e}");
103                 RebootReason::InvalidRamdisk
104             })?;
105 
106             // SAFETY: map_rodata validated the range to be in main memory, mapped, and not
107             // overlap.
108             Some(unsafe { slice::from_raw_parts(r.start as *const u8, r.len()) })
109         } else {
110             info!("Couldn't locate the ramdisk from the device tree");
111             None
112         };
113 
114         Ok(Self { fdt: untrusted_fdt, kernel, ramdisk })
115     }
116 }
117