blob: 48f73192ba19377d62246087567336547c03f276 [file] [log] [blame]
Neill Kaprondd93f852024-08-07 22:39:57 +00001/*
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 Kaprond036b6f2024-11-09 00:30:13 +000018
19// Enable dead_code until feature flag is removed.
20#![cfg_attr(not(enable_libbpf), allow(dead_code))]
21
22use android_ids::{AID_ROOT, AID_SYSTEM};
Neill Kaproncbb87b62024-10-04 17:18:45 +000023use android_logger::AndroidLogger;
Neill Kaprond036b6f2024-11-09 00:30:13 +000024use anyhow::{anyhow, ensure};
25use libbpf_rs::{MapCore, ObjectBuilder};
26use libc::{mode_t, S_IRGRP, S_IRUSR, S_IWGRP, S_IWUSR};
27use log::{debug, error, info, Level, LevelFilter, Log, Metadata, Record, SetLoggerError};
Neill Kaproncbb87b62024-10-04 17:18:45 +000028use std::{
29 cmp::max,
Neill Kaprond036b6f2024-11-09 00:30:13 +000030 env, fs,
31 fs::{File, Permissions},
Neill Kaproncbb87b62024-10-04 17:18:45 +000032 io::{LineWriter, Write},
33 os::fd::FromRawFd,
Neill Kaprond036b6f2024-11-09 00:30:13 +000034 os::unix::fs::{chown, PermissionsExt},
Neill Kaproncbb87b62024-10-04 17:18:45 +000035 panic,
Neill Kaprond036b6f2024-11-09 00:30:13 +000036 path::Path,
Neill Kaproncbb87b62024-10-04 17:18:45 +000037 sync::{Arc, Mutex},
38};
39
40enum 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
52fn 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.
67pub struct BpfKmsgLogger {
68 log_level: LevelFilter,
69 tag: String,
70 kmsg_writer: Arc<Mutex<Box<dyn Write + Send>>>,
71 a_logger: AndroidLogger,
72}
73
74impl 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
102impl 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 Kaprondd93f852024-08-07 22:39:57 +0000124
Neill Kaprond036b6f2024-11-09 00:30:13 +0000125struct MapDesc {
126 name: &'static str,
127 perms: mode_t,
128}
129
130struct ProgDesc {
131 name: &'static str,
132}
133
134struct 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
145const PERM_GRW: mode_t = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
146const PERM_GRO: mode_t = S_IRUSR | S_IWUSR | S_IRGRP;
147const PERM_GWO: mode_t = S_IRUSR | S_IWUSR | S_IWGRP;
148const PERM_UGR: mode_t = S_IRUSR | S_IRGRP;
149
150const 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
179fn 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 Kapron01249cb2024-08-22 19:09:18 +0000264#[cfg(enable_libbpf)]
265fn load_libbpf_progs() {
Neill Kaproncbb87b62024-10-04 17:18:45 +0000266 info!("Loading libbpf programs");
Neill Kaprond036b6f2024-11-09 00:30:13 +0000267 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 Kapron01249cb2024-08-22 19:09:18 +0000276}
277
278#[cfg(not(enable_libbpf))]
279fn load_libbpf_progs() {
280 // Empty stub for feature flag disabled case
Neill Kaprond036b6f2024-11-09 00:30:13 +0000281 info!("Loading libbpf programs DISABLED");
Neill Kapron01249cb2024-08-22 19:09:18 +0000282}
283
Neill Kaprondd93f852024-08-07 22:39:57 +0000284fn main() {
Neill Kaproncbb87b62024-10-04 17:18:45 +0000285 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 Kapron01249cb2024-08-22 19:09:18 +0000298 load_libbpf_progs();
Neill Kaprond036b6f2024-11-09 00:30:13 +0000299 info!("Loading legacy BPF progs");
Neill Kapron01249cb2024-08-22 19:09:18 +0000300
Neill Kaprondd93f852024-08-07 22:39:57 +0000301 // SAFETY: Linking in the existing legacy bpfloader functionality.
Maciej Żenczykowski7ff83102024-08-13 20:04:00 +0000302 // Any of the four following bindgen functions can abort() or exit()
Neill Kaprondd93f852024-08-07 22:39:57 +0000303 // on failure and execNetBpfLoadDone() execve()'s.
304 unsafe {
305 bpf_android_bindgen::initLogging();
Maciej Żenczykowski7ff83102024-08-13 20:04:00 +0000306 bpf_android_bindgen::createBpfFsSubDirectories();
Neill Kaprondd93f852024-08-07 22:39:57 +0000307 bpf_android_bindgen::legacyBpfLoader();
308 bpf_android_bindgen::execNetBpfLoadDone();
309 }
310}