Jaewan Kim | ba8929b | 2023-01-13 11:13:29 +0900 | [diff] [blame] | 1 | // 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 Park | b87f330 | 2023-03-21 10:03:11 +0900 | [diff] [blame] | 17 | use crate::cstr; |
Jaewan Kim | c03f661 | 2023-02-20 00:06:26 +0900 | [diff] [blame] | 18 | use alloc::{vec, vec::Vec}; |
Jaewan Kim | ba8929b | 2023-01-13 11:13:29 +0900 | [diff] [blame] | 19 | use core::ffi::CStr; |
| 20 | use core::fmt; |
| 21 | use libfdt::FdtError; |
| 22 | use log::info; |
| 23 | |
| 24 | #[derive(Debug, Clone)] |
| 25 | pub 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 | |
| 34 | impl 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. |
| 51 | unsafe 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 Kim | c03f661 | 2023-02-20 00:06:26 +0900 | [diff] [blame] | 67 | /// Disables ramdump by removing crashkernel from bootargs in /chosen. |
| 68 | fn disable_ramdump(fdt: &mut libfdt::Fdt) -> Result<(), DebugPolicyError> { |
Jaewan Kim | ba8929b | 2023-01-13 11:13:29 +0900 | [diff] [blame] | 69 | let chosen = match fdt |
Jiyong Park | b87f330 | 2023-03-21 10:03:11 +0900 | [diff] [blame] | 70 | .node(cstr!("/chosen")) |
Jaewan Kim | ba8929b | 2023-01-13 11:13:29 +0900 | [diff] [blame] | 71 | .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 Park | b87f330 | 2023-03-21 10:03:11 +0900 | [diff] [blame] | 78 | .getprop_str(cstr!("bootargs")) |
Jaewan Kim | ba8929b | 2023-01-13 11:13:29 +0900 | [diff] [blame] | 79 | .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 Park | b87f330 | 2023-03-21 10:03:11 +0900 | [diff] [blame] | 101 | 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 Kim | ba8929b | 2023-01-13 11:13:29 +0900 | [diff] [blame] | 103 | 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> |
| 108 | fn is_ramdump_enabled(fdt: &libfdt::Fdt) -> Result<bool, DebugPolicyError> { |
| 109 | let common = match fdt |
Jiyong Park | b87f330 | 2023-03-21 10:03:11 +0900 | [diff] [blame] | 110 | .node(cstr!("/avf/guest/common")) |
Jaewan Kim | ba8929b | 2023-01-13 11:13:29 +0900 | [diff] [blame] | 111 | .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 Park | b87f330 | 2023-03-21 10:03:11 +0900 | [diff] [blame] | 118 | .getprop_u32(cstr!("ramdump")) |
Jaewan Kim | ba8929b | 2023-01-13 11:13:29 +0900 | [diff] [blame] | 119 | .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to find ramdump prop", e))? |
| 120 | { |
| 121 | Some(1) => Ok(true), |
| 122 | _ => Ok(false), |
| 123 | } |
| 124 | } |
| 125 | |
Jaewan Kim | c03f661 | 2023-02-20 00:06:26 +0900 | [diff] [blame] | 126 | /// 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. |
| 128 | fn enable_console_output(fdt: &mut libfdt::Fdt) -> Result<(), DebugPolicyError> { |
Jaewan Kim | c03f661 | 2023-02-20 00:06:26 +0900 | [diff] [blame] | 129 | let chosen = match fdt |
Jiyong Park | b87f330 | 2023-03-21 10:03:11 +0900 | [diff] [blame] | 130 | .node(cstr!("/chosen")) |
Jaewan Kim | c03f661 | 2023-02-20 00:06:26 +0900 | [diff] [blame] | 131 | .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 Park | b87f330 | 2023-03-21 10:03:11 +0900 | [diff] [blame] | 138 | .getprop_str(cstr!("bootargs")) |
Jaewan Kim | c03f661 | 2023-02-20 00:06:26 +0900 | [diff] [blame] | 139 | .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 Park | b87f330 | 2023-03-21 10:03:11 +0900 | [diff] [blame] | 152 | 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 Kim | c03f661 | 2023-02-20 00:06:26 +0900 | [diff] [blame] | 154 | 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> |
| 162 | fn is_console_output_enabled(fdt: &libfdt::Fdt) -> Result<bool, DebugPolicyError> { |
| 163 | let common = match fdt |
Jiyong Park | b87f330 | 2023-03-21 10:03:11 +0900 | [diff] [blame] | 164 | .node(cstr!("/avf/guest/common")) |
Jaewan Kim | c03f661 | 2023-02-20 00:06:26 +0900 | [diff] [blame] | 165 | .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 Park | b87f330 | 2023-03-21 10:03:11 +0900 | [diff] [blame] | 172 | .getprop_u32(cstr!("log")) |
Jaewan Kim | c03f661 | 2023-02-20 00:06:26 +0900 | [diff] [blame] | 173 | .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to find log prop", e))? |
| 174 | { |
| 175 | Some(1) => Ok(true), |
| 176 | _ => Ok(false), |
| 177 | } |
| 178 | } |
| 179 | |
Jaewan Kim | ba8929b | 2023-01-13 11:13:29 +0900 | [diff] [blame] | 180 | /// Handles debug policies. |
| 181 | /// |
| 182 | /// # Safety |
| 183 | /// |
| 184 | /// This may corrupt the input `Fdt` when overlaying debug policy or applying |
| 185 | /// ramdump configuration. |
| 186 | pub 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 Kim | c03f661 | 2023-02-20 00:06:26 +0900 | [diff] [blame] | 197 | } else { |
| 198 | disable_ramdump(fdt)?; |
Jaewan Kim | ba8929b | 2023-01-13 11:13:29 +0900 | [diff] [blame] | 199 | } |
Jaewan Kim | c03f661 | 2023-02-20 00:06:26 +0900 | [diff] [blame] | 200 | |
Jaewan Kim | 66f062e | 2023-02-25 01:07:43 +0900 | [diff] [blame] | 201 | // Handles console output in the debug policy |
Jaewan Kim | c03f661 | 2023-02-20 00:06:26 +0900 | [diff] [blame] | 202 | if is_console_output_enabled(fdt)? { |
| 203 | enable_console_output(fdt)?; |
| 204 | info!("console output is enabled by debug policy"); |
| 205 | } |
| 206 | Ok(()) |
Jaewan Kim | ba8929b | 2023-01-13 11:13:29 +0900 | [diff] [blame] | 207 | } |