linux_bootloader/
linux_loader.rs1use core::{ffi::c_void, pin::Pin, ptr::slice_from_raw_parts_mut};
9
10use alloc::{boxed::Box, vec::Vec};
11use uefi::{
12 prelude::BootServices,
13 proto::{
14 device_path::{DevicePath, FfiDevicePath},
15 unsafe_protocol,
16 },
17 Handle, Identify, Result, ResultExt, Status,
18};
19
20static mut DEVICE_PATH_PROTOCOL: [u8; 24] = [
44 0x04, 0x03, 0x14, 0x00, 0x27, 0xe4, 0x68, 0x55, 0xfc, 0x68, 0x3d, 0x4f, 0xac, 0x74, 0xca, 0x55,
45 0x52, 0x31, 0xcc, 0x68, 0x7f, 0xff, 0x04, 0x00,
46];
47
48#[repr(C)]
52#[unsafe_protocol("4006c0c1-fcb3-403e-996d-4a6c8724e06d")]
53struct LoadFile2Protocol {
54 load_file: unsafe extern "efiapi" fn(
55 this: &mut LoadFile2Protocol,
56 file_path: *const FfiDevicePath,
57 boot_policy: bool,
58 buffer_size: *mut usize,
59 buffer: *mut c_void,
60 ) -> Status,
61
62 initrd_data: Vec<u8>,
64}
65
66impl LoadFile2Protocol {
67 fn load_file(
68 &mut self,
69 _file_path: *const FfiDevicePath,
70 _boot_policy: bool,
71 buffer_size: *mut usize,
72 buffer: *mut c_void,
73 ) -> Result<()> {
74 if buffer.is_null() || unsafe { *buffer_size } < self.initrd_data.len() {
75 unsafe {
76 *buffer_size = self.initrd_data.len();
77 }
78 return Err(Status::BUFFER_TOO_SMALL.into());
79 };
80
81 unsafe {
82 *buffer_size = self.initrd_data.len();
83 }
84
85 let output_slice: &mut [u8] =
86 unsafe { &mut *slice_from_raw_parts_mut(buffer as *mut u8, *buffer_size) };
87
88 output_slice.copy_from_slice(&self.initrd_data);
89
90 Ok(())
91 }
92}
93
94unsafe extern "efiapi" fn raw_load_file(
95 this: &mut LoadFile2Protocol,
96 file_path: *const FfiDevicePath,
97 boot_policy: bool,
98 buffer_size: *mut usize,
99 buffer: *mut c_void,
100) -> Status {
101 this.load_file(file_path, boot_policy, buffer_size, buffer)
102 .status()
103}
104
105pub struct InitrdLoader {
111 proto: Pin<Box<LoadFile2Protocol>>,
112 handle: Handle,
113 registered: bool,
114}
115
116impl InitrdLoader {
117 pub fn new(boot_services: &BootServices, handle: Handle, initrd_data: Vec<u8>) -> Result<Self> {
122 let mut proto = Box::pin(LoadFile2Protocol {
123 load_file: raw_load_file,
124 initrd_data,
125 });
126
127 unsafe {
131 let dp_proto: *mut u8 = DEVICE_PATH_PROTOCOL.as_mut_ptr();
132
133 boot_services.install_protocol_interface(
134 Some(handle),
135 &DevicePath::GUID,
136 dp_proto as *mut c_void,
137 )?;
138
139 let lf_proto: *mut LoadFile2Protocol = proto.as_mut().get_mut();
140
141 boot_services.install_protocol_interface(
142 Some(handle),
143 &LoadFile2Protocol::GUID,
144 lf_proto as *mut c_void,
145 )?;
146 }
147
148 Ok(InitrdLoader {
149 handle,
150 proto,
151 registered: true,
152 })
153 }
154
155 pub fn uninstall(&mut self, boot_services: &BootServices) -> Result<()> {
156 assert!(self.registered);
158
159 unsafe {
160 let dp_proto: *mut u8 = &mut DEVICE_PATH_PROTOCOL[0];
161 boot_services.uninstall_protocol_interface(
162 self.handle,
163 &DevicePath::GUID,
164 dp_proto as *mut c_void,
165 )?;
166
167 let lf_proto: *mut LoadFile2Protocol = self.proto.as_mut().get_mut();
168
169 boot_services.uninstall_protocol_interface(
170 self.handle,
171 &LoadFile2Protocol::GUID,
172 lf_proto as *mut c_void,
173 )?;
174 }
175
176 self.registered = false;
177
178 Ok(())
179 }
180}
181
182impl Drop for InitrdLoader {
183 fn drop(&mut self) {
184 assert!(!self.registered);
186 }
187}