blob: 37e2af80ef5ca157ccc3fa11718c6f91874da28e [file] [log] [blame]
Jaewan Kimba8929b2023-01-13 11:13:29 +09001// Copyright 2023, 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//! Support for the debug policy overlay in pvmfw
16
17use alloc::vec;
18use core::ffi::CStr;
19use core::fmt;
20use libfdt::FdtError;
21use log::info;
22
23#[derive(Debug, Clone)]
24pub enum DebugPolicyError {
25 /// The provided baseline FDT was invalid or malformed, so cannot access certain node/prop
26 Fdt(&'static str, FdtError),
27 /// The provided debug policy FDT was invalid or malformed.
28 DebugPolicyFdt(&'static str, FdtError),
29 /// The overlaid result FDT is invalid or malformed, and may be corrupted.
30 OverlaidFdt(&'static str, FdtError),
31}
32
33impl fmt::Display for DebugPolicyError {
34 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35 match self {
36 Self::Fdt(s, e) => write!(f, "Invalid baseline FDT. {s}: {e}"),
37 Self::DebugPolicyFdt(s, e) => write!(f, "Invalid overlay FDT. {s}: {e}"),
38 Self::OverlaidFdt(s, e) => write!(f, "Invalid overlaid FDT. {s}: {e}"),
39 }
40 }
41}
42
43/// Applies the debug policy device tree overlay to the pVM DT.
44///
45/// # Safety
46///
47/// When an error is returned by this function, the input `Fdt` should be
48/// discarded as it may have have been partially corrupted during the overlay
49/// application process.
50unsafe fn apply_debug_policy(
51 fdt: &mut libfdt::Fdt,
52 debug_policy: &mut [u8],
53) -> Result<(), DebugPolicyError> {
54 let overlay = libfdt::Fdt::from_mut_slice(debug_policy)
55 .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to load debug policy overlay", e))?;
56
57 fdt.unpack().map_err(|e| DebugPolicyError::Fdt("Failed to unpack", e))?;
58
59 let fdt = fdt
60 .apply_overlay(overlay)
61 .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to apply overlay", e))?;
62
63 fdt.pack().map_err(|e| DebugPolicyError::OverlaidFdt("Failed to re-pack", e))
64}
65
66/// Dsiables ramdump by removing crashkernel from bootargs in /chosen.
67///
68/// # Safety
69///
70/// This may corrupt the input `Fdt` when error happens while editing prop value.
71unsafe fn disable_ramdump(fdt: &mut libfdt::Fdt) -> Result<(), DebugPolicyError> {
72 let chosen_path = CStr::from_bytes_with_nul(b"/chosen\0").unwrap();
73 let bootargs_name = CStr::from_bytes_with_nul(b"bootargs\0").unwrap();
74
75 let chosen = match fdt
76 .node(chosen_path)
77 .map_err(|e| DebugPolicyError::Fdt("Failed to find /chosen", e))?
78 {
79 Some(node) => node,
80 None => return Ok(()),
81 };
82
83 let bootargs = match chosen
84 .getprop_str(bootargs_name)
85 .map_err(|e| DebugPolicyError::Fdt("Failed to find bootargs prop", e))?
86 {
87 Some(value) if !value.to_bytes().is_empty() => value,
88 _ => return Ok(()),
89 };
90
91 // TODO: Improve add 'crashkernel=17MB' only when it's unnecessary.
92 // Currently 'crashkernel=17MB' in virtualizationservice and passed by
93 // chosen node, because it's not exactly a debug policy but a
94 // configuration. However, it's actually microdroid specific
95 // so we need a way to generalize it.
96 let mut args = vec![];
97 for arg in bootargs.to_bytes().split(|byte| byte.is_ascii_whitespace()) {
98 if arg.is_empty() || arg.starts_with(b"crashkernel=") {
99 continue;
100 }
101 args.push(arg);
102 }
103 let mut new_bootargs = args.as_slice().join(&b" "[..]);
104 new_bootargs.push(b'\0');
105
106 // We've checked existence of /chosen node at the beginning.
107 let mut chosen_mut = fdt.node_mut(chosen_path).unwrap().unwrap();
108 chosen_mut.setprop(bootargs_name, new_bootargs.as_slice()).map_err(|e| {
109 DebugPolicyError::OverlaidFdt("Failed to remove crashkernel. FDT might be corrupted", e)
110 })
111}
112
113/// Returns true only if fdt has ramdump prop in the /avf/guest/common node with value <1>
114fn is_ramdump_enabled(fdt: &libfdt::Fdt) -> Result<bool, DebugPolicyError> {
115 let common = match fdt
116 .node(CStr::from_bytes_with_nul(b"/avf/guest/common\0").unwrap())
117 .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to find /avf/guest/common node", e))?
118 {
119 Some(node) => node,
120 None => return Ok(false),
121 };
122
123 match common
124 .getprop_u32(CStr::from_bytes_with_nul(b"ramdump\0").unwrap())
125 .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to find ramdump prop", e))?
126 {
127 Some(1) => Ok(true),
128 _ => Ok(false),
129 }
130}
131
132/// Handles debug policies.
133///
134/// # Safety
135///
136/// This may corrupt the input `Fdt` when overlaying debug policy or applying
137/// ramdump configuration.
138pub unsafe fn handle_debug_policy(
139 fdt: &mut libfdt::Fdt,
140 debug_policy: Option<&mut [u8]>,
141) -> Result<(), DebugPolicyError> {
142 if let Some(dp) = debug_policy {
143 apply_debug_policy(fdt, dp)?;
144 }
145
146 // Handles ramdump in the debug policy
147 if is_ramdump_enabled(fdt)? {
148 info!("ramdump is enabled by debug policy");
149 return Ok(());
150 }
151 disable_ramdump(fdt)
152}