Neill Kapron | dd93f85 | 2024-08-07 22:39:57 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2024 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | //! BPF loader for system and vendor applications |
Neill Kapron | d036b6f | 2024-11-09 00:30:13 +0000 | [diff] [blame] | 18 | |
| 19 | // Enable dead_code until feature flag is removed. |
| 20 | #![cfg_attr(not(enable_libbpf), allow(dead_code))] |
| 21 | |
| 22 | use android_ids::{AID_ROOT, AID_SYSTEM}; |
Neill Kapron | cbb87b6 | 2024-10-04 17:18:45 +0000 | [diff] [blame] | 23 | use android_logger::AndroidLogger; |
Neill Kapron | d036b6f | 2024-11-09 00:30:13 +0000 | [diff] [blame] | 24 | use anyhow::{anyhow, ensure}; |
| 25 | use libbpf_rs::{MapCore, ObjectBuilder}; |
| 26 | use libc::{mode_t, S_IRGRP, S_IRUSR, S_IWGRP, S_IWUSR}; |
| 27 | use log::{debug, error, info, Level, LevelFilter, Log, Metadata, Record, SetLoggerError}; |
Neill Kapron | cbb87b6 | 2024-10-04 17:18:45 +0000 | [diff] [blame] | 28 | use std::{ |
| 29 | cmp::max, |
Neill Kapron | d036b6f | 2024-11-09 00:30:13 +0000 | [diff] [blame] | 30 | env, fs, |
| 31 | fs::{File, Permissions}, |
Neill Kapron | cbb87b6 | 2024-10-04 17:18:45 +0000 | [diff] [blame] | 32 | io::{LineWriter, Write}, |
| 33 | os::fd::FromRawFd, |
Neill Kapron | d036b6f | 2024-11-09 00:30:13 +0000 | [diff] [blame] | 34 | os::unix::fs::{chown, PermissionsExt}, |
Neill Kapron | cbb87b6 | 2024-10-04 17:18:45 +0000 | [diff] [blame] | 35 | panic, |
Neill Kapron | d036b6f | 2024-11-09 00:30:13 +0000 | [diff] [blame] | 36 | path::Path, |
Neill Kapron | cbb87b6 | 2024-10-04 17:18:45 +0000 | [diff] [blame] | 37 | sync::{Arc, Mutex}, |
| 38 | }; |
| 39 | |
| 40 | enum KernelLevel { |
| 41 | // Commented out unused due to rust complaining... |
| 42 | // EMERG = 0, |
| 43 | // ALERT = 1, |
| 44 | // CRIT = 2, |
| 45 | ERR = 3, |
| 46 | WARNING = 4, |
| 47 | // NOTICE = 5, |
| 48 | INFO = 6, |
| 49 | DEBUG = 7, |
| 50 | } |
| 51 | |
| 52 | fn level_to_kern_level(level: &Level) -> u8 { |
| 53 | let result = match level { |
| 54 | Level::Error => KernelLevel::ERR, |
| 55 | Level::Warn => KernelLevel::WARNING, |
| 56 | Level::Info => KernelLevel::INFO, |
| 57 | Level::Debug => KernelLevel::DEBUG, |
| 58 | Level::Trace => KernelLevel::DEBUG, |
| 59 | }; |
| 60 | result as u8 |
| 61 | } |
| 62 | |
| 63 | /// A logger implementation to enable bpfloader to write to kmsg on error as |
| 64 | /// bpfloader runs at early init prior to the availability of standard Android |
| 65 | /// logging. If a crash were to occur, we can disrupt boot, and therefore we |
| 66 | /// need the ability to access the logs on the serial port. |
| 67 | pub struct BpfKmsgLogger { |
| 68 | log_level: LevelFilter, |
| 69 | tag: String, |
| 70 | kmsg_writer: Arc<Mutex<Box<dyn Write + Send>>>, |
| 71 | a_logger: AndroidLogger, |
| 72 | } |
| 73 | |
| 74 | impl Log for BpfKmsgLogger { |
| 75 | fn enabled(&self, metadata: &Metadata) -> bool { |
| 76 | metadata.level() <= self.log_level || self.a_logger.enabled(metadata) |
| 77 | } |
| 78 | |
| 79 | fn log(&self, record: &Record) { |
| 80 | if !self.enabled(record.metadata()) { |
| 81 | return; |
| 82 | } |
| 83 | |
| 84 | if record.metadata().level() <= self.log_level { |
| 85 | let mut writer = self.kmsg_writer.lock().unwrap(); |
| 86 | write!( |
| 87 | writer, |
| 88 | "<{}>{}: {}", |
| 89 | level_to_kern_level(&record.level()), |
| 90 | self.tag, |
| 91 | record.args() |
| 92 | ) |
| 93 | .unwrap(); |
| 94 | let _ = writer.flush(); |
| 95 | } |
| 96 | self.a_logger.log(record); |
| 97 | } |
| 98 | |
| 99 | fn flush(&self) {} |
| 100 | } |
| 101 | |
| 102 | impl BpfKmsgLogger { |
| 103 | /// Initialize the logger |
| 104 | pub fn init(kmsg_file: File) -> Result<(), SetLoggerError> { |
| 105 | let alog_level = LevelFilter::Info; |
| 106 | let kmsg_level = LevelFilter::Error; |
| 107 | |
| 108 | let log_config = android_logger::Config::default() |
| 109 | .with_tag("BpfLoader-rs") |
| 110 | .with_max_level(alog_level) |
| 111 | .with_log_buffer(android_logger::LogId::Main) |
| 112 | .format(|buf, record| writeln!(buf, "{}", record.args())); |
| 113 | |
| 114 | let writer = Box::new(LineWriter::new(kmsg_file)) as Box<dyn Write + Send>; |
| 115 | log::set_max_level(max(alog_level, kmsg_level)); |
| 116 | log::set_boxed_logger(Box::new(BpfKmsgLogger { |
| 117 | log_level: kmsg_level, |
| 118 | tag: "BpfLoader-rs".to_string(), |
| 119 | kmsg_writer: Arc::new(Mutex::new(writer)), |
| 120 | a_logger: AndroidLogger::new(log_config), |
| 121 | })) |
| 122 | } |
| 123 | } |
Neill Kapron | dd93f85 | 2024-08-07 22:39:57 +0000 | [diff] [blame] | 124 | |
Neill Kapron | d036b6f | 2024-11-09 00:30:13 +0000 | [diff] [blame] | 125 | struct MapDesc { |
| 126 | name: &'static str, |
| 127 | perms: mode_t, |
| 128 | } |
| 129 | |
| 130 | struct ProgDesc { |
| 131 | name: &'static str, |
| 132 | } |
| 133 | |
| 134 | struct BpfFileDesc { |
| 135 | filename: &'static str, |
| 136 | // Warning: setting this to 'true' will cause the system to boot loop if there are any issues |
| 137 | // loading the bpf program. |
| 138 | critical: bool, |
| 139 | owner: u32, |
| 140 | group: u32, |
| 141 | maps: &'static [MapDesc], |
| 142 | progs: &'static [ProgDesc], |
| 143 | } |
| 144 | |
| 145 | const PERM_GRW: mode_t = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; |
| 146 | const PERM_GRO: mode_t = S_IRUSR | S_IWUSR | S_IRGRP; |
| 147 | const PERM_GWO: mode_t = S_IRUSR | S_IWUSR | S_IWGRP; |
| 148 | const PERM_UGR: mode_t = S_IRUSR | S_IRGRP; |
| 149 | |
| 150 | const FILE_ARR: &[BpfFileDesc] = &[BpfFileDesc { |
| 151 | filename: "timeInState.bpf", |
| 152 | critical: false, |
| 153 | owner: AID_ROOT, |
| 154 | group: AID_SYSTEM, |
| 155 | maps: &[ |
| 156 | MapDesc { name: "cpu_last_pid_map", perms: PERM_GWO }, |
| 157 | MapDesc { name: "cpu_last_update_map", perms: PERM_GWO }, |
| 158 | MapDesc { name: "cpu_policy_map", perms: PERM_GWO }, |
| 159 | MapDesc { name: "freq_to_idx_map", perms: PERM_GWO }, |
| 160 | MapDesc { name: "nr_active_map", perms: PERM_GWO }, |
| 161 | MapDesc { name: "pid_task_aggregation_map", perms: PERM_GWO }, |
| 162 | MapDesc { name: "pid_time_in_state_map", perms: PERM_GRO }, |
| 163 | MapDesc { name: "pid_tracked_hash_map", perms: PERM_GWO }, |
| 164 | MapDesc { name: "pid_tracked_map", perms: PERM_GWO }, |
| 165 | MapDesc { name: "policy_freq_idx_map", perms: PERM_GWO }, |
| 166 | MapDesc { name: "policy_nr_active_map", perms: PERM_GWO }, |
| 167 | MapDesc { name: "total_time_in_state_map", perms: PERM_GRW }, |
| 168 | MapDesc { name: "uid_concurrent_times_map", perms: PERM_GRW }, |
| 169 | MapDesc { name: "uid_last_update_map", perms: PERM_GRW }, |
| 170 | MapDesc { name: "uid_time_in_state_map", perms: PERM_GRW }, |
| 171 | ], |
| 172 | progs: &[ |
| 173 | ProgDesc { name: "tracepoint_power_cpu_frequency" }, |
| 174 | ProgDesc { name: "tracepoint_sched_sched_process_free" }, |
| 175 | ProgDesc { name: "tracepoint_sched_sched_switch" }, |
| 176 | ], |
| 177 | }]; |
| 178 | |
| 179 | fn libbpf_worker(file_desc: &BpfFileDesc) -> Result<(), anyhow::Error> { |
| 180 | info!("Loading {}", file_desc.filename); |
| 181 | let filepath = Path::new("/etc/bpf/").join(file_desc.filename); |
| 182 | ensure!(filepath.exists(), "File not found {}", filepath.display()); |
| 183 | let filename = |
| 184 | filepath.file_stem().ok_or_else(|| anyhow!("Failed to parse stem from filename"))?; |
| 185 | let filename = filename.to_str().ok_or_else(|| anyhow!("Failed to parse filename"))?; |
| 186 | |
| 187 | let mut ob = ObjectBuilder::default(); |
| 188 | let open_file = ob.open_file(&filepath)?; |
| 189 | let mut loaded_file = open_file.load()?; |
| 190 | |
| 191 | let bpffs_path = "/sys/fs/bpf/".to_owned(); |
| 192 | |
| 193 | for mut map in loaded_file.maps_mut() { |
| 194 | let mut desc_found = false; |
| 195 | let name = |
| 196 | map.name().to_str().ok_or_else(|| anyhow!("Failed to parse map name into UTF-8"))?; |
| 197 | let name = String::from(name); |
| 198 | for map_desc in file_desc.maps { |
| 199 | if map_desc.name == name { |
| 200 | desc_found = true; |
| 201 | let pinpath_str = bpffs_path.clone() + "map_" + filename + "_" + &name; |
| 202 | let pinpath = Path::new(&pinpath_str); |
| 203 | debug!("Pinning: {}", pinpath.display()); |
| 204 | map.pin(pinpath).map_err(|e| anyhow!("Failed to pin map {name}: {e}"))?; |
| 205 | fs::set_permissions(pinpath, Permissions::from_mode(map_desc.perms as _)).map_err( |
| 206 | |e| { |
| 207 | anyhow!( |
| 208 | "Failed to set permissions: {} on pinned map {}: {e}", |
| 209 | map_desc.perms, |
| 210 | pinpath.display() |
| 211 | ) |
| 212 | }, |
| 213 | )?; |
| 214 | chown(pinpath, Some(file_desc.owner), Some(file_desc.group)).map_err(|e| { |
| 215 | anyhow!( |
| 216 | "Failed to chown {} with owner: {} group: {} err: {e}", |
| 217 | pinpath.display(), |
| 218 | file_desc.owner, |
| 219 | file_desc.group |
| 220 | ) |
| 221 | })?; |
| 222 | break; |
| 223 | } |
| 224 | } |
| 225 | ensure!(desc_found, "Descriptor for {name} not found!"); |
| 226 | } |
| 227 | |
| 228 | for mut prog in loaded_file.progs_mut() { |
| 229 | let mut desc_found = false; |
| 230 | let name = |
| 231 | prog.name().to_str().ok_or_else(|| anyhow!("Failed to parse prog name into UTF-8"))?; |
| 232 | let name = String::from(name); |
| 233 | for prog_desc in file_desc.progs { |
| 234 | if prog_desc.name == name { |
| 235 | desc_found = true; |
| 236 | let pinpath_str = bpffs_path.clone() + "prog_" + filename + "_" + &name; |
| 237 | let pinpath = Path::new(&pinpath_str); |
| 238 | debug!("Pinning: {}", pinpath.display()); |
| 239 | prog.pin(pinpath).map_err(|e| anyhow!("Failed to pin prog {name}: {e}"))?; |
| 240 | fs::set_permissions(pinpath, Permissions::from_mode(PERM_UGR as _)).map_err( |
| 241 | |e| { |
| 242 | anyhow!( |
| 243 | "Failed to set permissions on pinned prog {}: {e}", |
| 244 | pinpath.display() |
| 245 | ) |
| 246 | }, |
| 247 | )?; |
| 248 | chown(pinpath, Some(file_desc.owner), Some(file_desc.group)).map_err(|e| { |
| 249 | anyhow!( |
| 250 | "Failed to chown {} with owner: {} group: {} err: {e}", |
| 251 | pinpath.display(), |
| 252 | file_desc.owner, |
| 253 | file_desc.group |
| 254 | ) |
| 255 | })?; |
| 256 | break; |
| 257 | } |
| 258 | } |
| 259 | ensure!(desc_found, "Descriptor for {name} not found!"); |
| 260 | } |
| 261 | Ok(()) |
| 262 | } |
| 263 | |
Neill Kapron | 01249cb | 2024-08-22 19:09:18 +0000 | [diff] [blame] | 264 | #[cfg(enable_libbpf)] |
| 265 | fn load_libbpf_progs() { |
Neill Kapron | cbb87b6 | 2024-10-04 17:18:45 +0000 | [diff] [blame] | 266 | info!("Loading libbpf programs"); |
Neill Kapron | d036b6f | 2024-11-09 00:30:13 +0000 | [diff] [blame] | 267 | for file_desc in FILE_ARR { |
| 268 | if let Err(e) = libbpf_worker(file_desc) { |
| 269 | if file_desc.critical { |
| 270 | panic!("Error when loading {0}: {e}", file_desc.filename); |
| 271 | } else { |
| 272 | error!("Error when loading {0}: {e}", file_desc.filename); |
| 273 | } |
| 274 | }; |
| 275 | } |
Neill Kapron | 01249cb | 2024-08-22 19:09:18 +0000 | [diff] [blame] | 276 | } |
| 277 | |
| 278 | #[cfg(not(enable_libbpf))] |
| 279 | fn load_libbpf_progs() { |
| 280 | // Empty stub for feature flag disabled case |
Neill Kapron | d036b6f | 2024-11-09 00:30:13 +0000 | [diff] [blame] | 281 | info!("Loading libbpf programs DISABLED"); |
Neill Kapron | 01249cb | 2024-08-22 19:09:18 +0000 | [diff] [blame] | 282 | } |
| 283 | |
Neill Kapron | dd93f85 | 2024-08-07 22:39:57 +0000 | [diff] [blame] | 284 | fn main() { |
Neill Kapron | cbb87b6 | 2024-10-04 17:18:45 +0000 | [diff] [blame] | 285 | let kmsg_fd = env::var("ANDROID_FILE__dev_kmsg").unwrap().parse::<i32>().unwrap(); |
| 286 | // SAFETY: The init script opens this file for us |
| 287 | let kmsg_file = unsafe { File::from_raw_fd(kmsg_fd) }; |
| 288 | |
| 289 | if let Err(logger) = BpfKmsgLogger::init(kmsg_file) { |
| 290 | error!("BpfLoader-rs: log::setlogger failed: {}", logger); |
| 291 | } |
| 292 | |
| 293 | // Redirect panic messages to both logcat and serial port |
| 294 | panic::set_hook(Box::new(|panic_info| { |
| 295 | error!("{}", panic_info); |
| 296 | })); |
| 297 | |
Neill Kapron | 01249cb | 2024-08-22 19:09:18 +0000 | [diff] [blame] | 298 | load_libbpf_progs(); |
Neill Kapron | d036b6f | 2024-11-09 00:30:13 +0000 | [diff] [blame] | 299 | info!("Loading legacy BPF progs"); |
Neill Kapron | 01249cb | 2024-08-22 19:09:18 +0000 | [diff] [blame] | 300 | |
Neill Kapron | dd93f85 | 2024-08-07 22:39:57 +0000 | [diff] [blame] | 301 | // SAFETY: Linking in the existing legacy bpfloader functionality. |
Maciej Żenczykowski | 7ff8310 | 2024-08-13 20:04:00 +0000 | [diff] [blame] | 302 | // Any of the four following bindgen functions can abort() or exit() |
Neill Kapron | dd93f85 | 2024-08-07 22:39:57 +0000 | [diff] [blame] | 303 | // on failure and execNetBpfLoadDone() execve()'s. |
| 304 | unsafe { |
| 305 | bpf_android_bindgen::initLogging(); |
Maciej Żenczykowski | 7ff8310 | 2024-08-13 20:04:00 +0000 | [diff] [blame] | 306 | bpf_android_bindgen::createBpfFsSubDirectories(); |
Neill Kapron | dd93f85 | 2024-08-07 22:39:57 +0000 | [diff] [blame] | 307 | bpf_android_bindgen::legacyBpfLoader(); |
| 308 | bpf_android_bindgen::execNetBpfLoadDone(); |
| 309 | } |
| 310 | } |