blob: 15efa1c43c2d5bf6b6c9dd91f59c68357a22466d [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
Jiyong Parkb87f3302023-03-21 10:03:11 +090017use crate::cstr;
Jaewan Kimc03f6612023-02-20 00:06:26 +090018use alloc::{vec, vec::Vec};
Jaewan Kimba8929b2023-01-13 11:13:29 +090019use core::ffi::CStr;
20use core::fmt;
21use libfdt::FdtError;
22use log::info;
23
24#[derive(Debug, Clone)]
25pub enum DebugPolicyError {
26 /// The provided baseline FDT was invalid or malformed, so cannot access certain node/prop
27 Fdt(&'static str, FdtError),
28 /// The provided debug policy FDT was invalid or malformed.
29 DebugPolicyFdt(&'static str, FdtError),
30 /// The overlaid result FDT is invalid or malformed, and may be corrupted.
31 OverlaidFdt(&'static str, FdtError),
32}
33
34impl fmt::Display for DebugPolicyError {
35 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36 match self {
37 Self::Fdt(s, e) => write!(f, "Invalid baseline FDT. {s}: {e}"),
38 Self::DebugPolicyFdt(s, e) => write!(f, "Invalid overlay FDT. {s}: {e}"),
39 Self::OverlaidFdt(s, e) => write!(f, "Invalid overlaid FDT. {s}: {e}"),
40 }
41 }
42}
43
44/// Applies the debug policy device tree overlay to the pVM DT.
45///
46/// # Safety
47///
48/// When an error is returned by this function, the input `Fdt` should be
49/// discarded as it may have have been partially corrupted during the overlay
50/// application process.
51unsafe fn apply_debug_policy(
52 fdt: &mut libfdt::Fdt,
53 debug_policy: &mut [u8],
54) -> Result<(), DebugPolicyError> {
55 let overlay = libfdt::Fdt::from_mut_slice(debug_policy)
56 .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to load debug policy overlay", e))?;
57
58 fdt.unpack().map_err(|e| DebugPolicyError::Fdt("Failed to unpack", e))?;
59
60 let fdt = fdt
61 .apply_overlay(overlay)
62 .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to apply overlay", e))?;
63
64 fdt.pack().map_err(|e| DebugPolicyError::OverlaidFdt("Failed to re-pack", e))
65}
66
Jaewan Kimc03f6612023-02-20 00:06:26 +090067/// Disables ramdump by removing crashkernel from bootargs in /chosen.
68fn disable_ramdump(fdt: &mut libfdt::Fdt) -> Result<(), DebugPolicyError> {
Jaewan Kimba8929b2023-01-13 11:13:29 +090069 let chosen = match fdt
Jiyong Parkb87f3302023-03-21 10:03:11 +090070 .node(cstr!("/chosen"))
Jaewan Kimba8929b2023-01-13 11:13:29 +090071 .map_err(|e| DebugPolicyError::Fdt("Failed to find /chosen", e))?
72 {
73 Some(node) => node,
74 None => return Ok(()),
75 };
76
77 let bootargs = match chosen
Jiyong Parkb87f3302023-03-21 10:03:11 +090078 .getprop_str(cstr!("bootargs"))
Jaewan Kimba8929b2023-01-13 11:13:29 +090079 .map_err(|e| DebugPolicyError::Fdt("Failed to find bootargs prop", e))?
80 {
81 Some(value) if !value.to_bytes().is_empty() => value,
82 _ => return Ok(()),
83 };
84
85 // TODO: Improve add 'crashkernel=17MB' only when it's unnecessary.
86 // Currently 'crashkernel=17MB' in virtualizationservice and passed by
87 // chosen node, because it's not exactly a debug policy but a
88 // configuration. However, it's actually microdroid specific
89 // so we need a way to generalize it.
90 let mut args = vec![];
91 for arg in bootargs.to_bytes().split(|byte| byte.is_ascii_whitespace()) {
92 if arg.is_empty() || arg.starts_with(b"crashkernel=") {
93 continue;
94 }
95 args.push(arg);
96 }
97 let mut new_bootargs = args.as_slice().join(&b" "[..]);
98 new_bootargs.push(b'\0');
99
100 // We've checked existence of /chosen node at the beginning.
Jiyong Parkb87f3302023-03-21 10:03:11 +0900101 let mut chosen_mut = fdt.node_mut(cstr!("/chosen")).unwrap().unwrap();
102 chosen_mut.setprop(cstr!("bootargs"), new_bootargs.as_slice()).map_err(|e| {
Jaewan Kimba8929b2023-01-13 11:13:29 +0900103 DebugPolicyError::OverlaidFdt("Failed to remove crashkernel. FDT might be corrupted", e)
104 })
105}
106
107/// Returns true only if fdt has ramdump prop in the /avf/guest/common node with value <1>
108fn is_ramdump_enabled(fdt: &libfdt::Fdt) -> Result<bool, DebugPolicyError> {
109 let common = match fdt
Jiyong Parkb87f3302023-03-21 10:03:11 +0900110 .node(cstr!("/avf/guest/common"))
Jaewan Kimba8929b2023-01-13 11:13:29 +0900111 .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to find /avf/guest/common node", e))?
112 {
113 Some(node) => node,
114 None => return Ok(false),
115 };
116
117 match common
Jiyong Parkb87f3302023-03-21 10:03:11 +0900118 .getprop_u32(cstr!("ramdump"))
Jaewan Kimba8929b2023-01-13 11:13:29 +0900119 .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to find ramdump prop", e))?
120 {
121 Some(1) => Ok(true),
122 _ => Ok(false),
123 }
124}
125
Jaewan Kimc03f6612023-02-20 00:06:26 +0900126/// Enables console output by adding kernel.printk.devkmsg and kernel.console to bootargs.
127/// This uses hardcoded console name 'hvc0' and it should be match with microdroid's bootconfig.debuggable.
128fn enable_console_output(fdt: &mut libfdt::Fdt) -> Result<(), DebugPolicyError> {
Jaewan Kimc03f6612023-02-20 00:06:26 +0900129 let chosen = match fdt
Jiyong Parkb87f3302023-03-21 10:03:11 +0900130 .node(cstr!("/chosen"))
Jaewan Kimc03f6612023-02-20 00:06:26 +0900131 .map_err(|e| DebugPolicyError::Fdt("Failed to find /chosen", e))?
132 {
133 Some(node) => node,
134 None => return Ok(()),
135 };
136
137 let bootargs = match chosen
Jiyong Parkb87f3302023-03-21 10:03:11 +0900138 .getprop_str(cstr!("bootargs"))
Jaewan Kimc03f6612023-02-20 00:06:26 +0900139 .map_err(|e| DebugPolicyError::Fdt("Failed to find bootargs prop", e))?
140 {
141 Some(value) if !value.to_bytes().is_empty() => value,
142 _ => return Ok(()),
143 };
144
145 let mut new_bootargs = Vec::from(bootargs.to_bytes());
146 new_bootargs.extend_from_slice(b" printk.devkmsg=on console=hvc0\0");
147
148 // We'll set larger prop, and need to prepare some room first.
149 fdt.unpack().map_err(|e| DebugPolicyError::OverlaidFdt("Failed to unpack", e))?;
150
151 // We've checked existence of /chosen node at the beginning.
Jiyong Parkb87f3302023-03-21 10:03:11 +0900152 let mut chosen_mut = fdt.node_mut(cstr!("/chosen")).unwrap().unwrap();
153 chosen_mut.setprop(cstr!("bootargs"), new_bootargs.as_slice()).map_err(|e| {
Jaewan Kimc03f6612023-02-20 00:06:26 +0900154 DebugPolicyError::OverlaidFdt("Failed to enabled console output. FDT might be corrupted", e)
155 })?;
156
157 fdt.pack().map_err(|e| DebugPolicyError::OverlaidFdt("Failed to pack", e))?;
158 Ok(())
159}
160
161/// Returns true only if fdt has log prop in the /avf/guest/common node with value <1>
162fn is_console_output_enabled(fdt: &libfdt::Fdt) -> Result<bool, DebugPolicyError> {
163 let common = match fdt
Jiyong Parkb87f3302023-03-21 10:03:11 +0900164 .node(cstr!("/avf/guest/common"))
Jaewan Kimc03f6612023-02-20 00:06:26 +0900165 .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to find /avf/guest/common node", e))?
166 {
167 Some(node) => node,
168 None => return Ok(false),
169 };
170
171 match common
Jiyong Parkb87f3302023-03-21 10:03:11 +0900172 .getprop_u32(cstr!("log"))
Jaewan Kimc03f6612023-02-20 00:06:26 +0900173 .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to find log prop", e))?
174 {
175 Some(1) => Ok(true),
176 _ => Ok(false),
177 }
178}
179
Jaewan Kimba8929b2023-01-13 11:13:29 +0900180/// Handles debug policies.
181///
182/// # Safety
183///
184/// This may corrupt the input `Fdt` when overlaying debug policy or applying
185/// ramdump configuration.
186pub unsafe fn handle_debug_policy(
187 fdt: &mut libfdt::Fdt,
188 debug_policy: Option<&mut [u8]>,
189) -> Result<(), DebugPolicyError> {
190 if let Some(dp) = debug_policy {
191 apply_debug_policy(fdt, dp)?;
192 }
193
194 // Handles ramdump in the debug policy
195 if is_ramdump_enabled(fdt)? {
196 info!("ramdump is enabled by debug policy");
Jaewan Kimc03f6612023-02-20 00:06:26 +0900197 } else {
198 disable_ramdump(fdt)?;
Jaewan Kimba8929b2023-01-13 11:13:29 +0900199 }
Jaewan Kimc03f6612023-02-20 00:06:26 +0900200
Jaewan Kim66f062e2023-02-25 01:07:43 +0900201 // Handles console output in the debug policy
Jaewan Kimc03f6612023-02-20 00:06:26 +0900202 if is_console_output_enabled(fdt)? {
203 enable_console_output(fdt)?;
204 info!("console output is enabled by debug policy");
205 }
206 Ok(())
Jaewan Kimba8929b2023-01-13 11:13:29 +0900207}