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
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};
23 use android_logger::AndroidLogger;
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};
28 use std::{
29 cmp::max,
30 env, fs,
31 fs::{File, Permissions},
32 io::{LineWriter, Write},
33 os::fd::FromRawFd,
34 os::unix::fs::{chown, PermissionsExt},
35 panic,
36 path::Path,
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
level_to_kern_level(level: &Level) -> u852 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 {
enabled(&self, metadata: &Metadata) -> bool75 fn enabled(&self, metadata: &Metadata) -> bool {
76 metadata.level() <= self.log_level || self.a_logger.enabled(metadata)
77 }
78
log(&self, record: &Record)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
flush(&self)99 fn flush(&self) {}
100 }
101
102 impl BpfKmsgLogger {
103 /// Initialize the logger
init(kmsg_file: File) -> Result<(), SetLoggerError>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 }
124
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
libbpf_worker(file_desc: &BpfFileDesc) -> Result<(), anyhow::Error>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
264 #[cfg(enable_libbpf)]
load_libbpf_progs()265 fn load_libbpf_progs() {
266 info!("Loading libbpf programs");
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 }
276 }
277
278 #[cfg(not(enable_libbpf))]
load_libbpf_progs()279 fn load_libbpf_progs() {
280 // Empty stub for feature flag disabled case
281 info!("Loading libbpf programs DISABLED");
282 }
283
main()284 fn main() {
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
298 load_libbpf_progs();
299 info!("Loading legacy BPF progs");
300
301 // SAFETY: Linking in the existing legacy bpfloader functionality.
302 // Any of the four following bindgen functions can abort() or exit()
303 // on failure and execNetBpfLoadDone() execve()'s.
304 unsafe {
305 bpf_android_bindgen::initLogging();
306 bpf_android_bindgen::createBpfFsSubDirectories();
307 bpf_android_bindgen::legacyBpfLoader();
308 bpf_android_bindgen::execNetBpfLoadDone();
309 }
310 }
311