xref: /aosp_15_r20/system/bpf/loader/bpfloader.rs (revision 55039e042b8390f50b0bdd70c11a2419f6d8fd50)
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