1use crate::boot::{BootImageInfo, BootImageLoader};
2use crate::error::Result;
3use crate::sys::{
4 XEN_ELFNOTE_ENTRY, XEN_ELFNOTE_HYPERCALL_PAGE, XEN_ELFNOTE_INIT_P2M, XEN_ELFNOTE_MOD_START_PFN,
5 XEN_ELFNOTE_PADDR_OFFSET, XEN_ELFNOTE_PHYS32_ENTRY, XEN_ELFNOTE_TYPES, XEN_ELFNOTE_VIRT_BASE,
6};
7use crate::Error;
8use elf::abi::{PF_R, PF_W, PF_X, PT_LOAD, SHT_NOTE};
9use elf::endian::AnyEndian;
10use elf::note::Note;
11use elf::ElfBytes;
12use flate2::bufread::GzDecoder;
13use log::debug;
14use memchr::memmem::find_iter;
15use slice_copy::copy;
16use std::collections::HashMap;
17use std::io::{BufReader, Read};
18use std::mem::size_of;
19use std::sync::Arc;
20use xz2::bufread::XzDecoder;
21
22const ELF_MAGIC: &[u8] = &[
23 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
24];
25const GZIP_MAGIC: &[u8] = &[0x1f, 0x8b];
26const XZ_MAGIC: &[u8] = &[0xfd, 0x37, 0x7a, 0x58];
27
28#[derive(Clone)]
29pub struct ElfImageLoader {
30 data: Arc<Vec<u8>>,
31}
32
33fn xen_note_value_as_u64(endian: AnyEndian, value: &[u8]) -> Option<u64> {
34 let bytes = value.to_vec();
35 match value.len() {
36 1 => {
37 let bytes: Option<[u8; size_of::<u8>()]> = bytes.try_into().ok();
38 Some(match endian {
39 AnyEndian::Little => u8::from_le_bytes(bytes?),
40 AnyEndian::Big => u8::from_be_bytes(bytes?),
41 } as u64)
42 }
43 2 => {
44 let bytes: Option<[u8; size_of::<u16>()]> = bytes.try_into().ok();
45 Some(match endian {
46 AnyEndian::Little => u16::from_le_bytes(bytes?),
47 AnyEndian::Big => u16::from_be_bytes(bytes?),
48 } as u64)
49 }
50 4 => {
51 let bytes: Option<[u8; size_of::<u32>()]> = bytes.try_into().ok();
52 Some(match endian {
53 AnyEndian::Little => u32::from_le_bytes(bytes?),
54 AnyEndian::Big => u32::from_be_bytes(bytes?),
55 } as u64)
56 }
57 8 => {
58 let bytes: Option<[u8; size_of::<u64>()]> = bytes.try_into().ok();
59 Some(match endian {
60 AnyEndian::Little => u64::from_le_bytes(bytes?),
61 AnyEndian::Big => u64::from_be_bytes(bytes?),
62 })
63 }
64 _ => None,
65 }
66}
67
68impl ElfImageLoader {
69 pub fn new(data: Arc<Vec<u8>>) -> ElfImageLoader {
70 ElfImageLoader { data }
71 }
72
73 pub fn load_gz(data: &[u8]) -> Result<ElfImageLoader> {
74 let buff = BufReader::new(data);
75 let image = ElfImageLoader::read_one_stream(&mut GzDecoder::new(buff))?;
76 Ok(ElfImageLoader::new(Arc::new(image)))
77 }
78
79 pub fn load_xz(data: &[u8]) -> Result<ElfImageLoader> {
80 let buff = BufReader::new(data);
81 let image = ElfImageLoader::read_one_stream(&mut XzDecoder::new(buff))?;
82 Ok(ElfImageLoader::new(Arc::new(image)))
83 }
84
85 pub fn load(data: Arc<Vec<u8>>) -> Result<ElfImageLoader> {
86 if data.len() >= 16 && find_iter(&data[0..15], ELF_MAGIC).next().is_some() {
87 return Ok(ElfImageLoader::new(data));
88 }
89
90 for start in find_iter(&data, GZIP_MAGIC) {
91 if let Ok(elf) = ElfImageLoader::load_gz(&data[start..]) {
92 return Ok(elf);
93 }
94 }
95
96 for start in find_iter(&data, XZ_MAGIC) {
97 if let Ok(elf) = ElfImageLoader::load_xz(&data[start..]) {
98 return Ok(elf);
99 }
100 }
101
102 Err(Error::ElfCompressionUnknown)
103 }
104
105 fn read_one_stream(read: &mut dyn Read) -> Result<Vec<u8>> {
106 let mut result: Vec<u8> = Vec::new();
107 let mut buffer = [0u8; 8192];
108
109 loop {
110 match read.read(&mut buffer) {
111 Ok(size) => {
112 if size == 0 {
113 break;
114 }
115 result.extend_from_slice(&buffer[0..size])
116 }
117 Err(error) => {
118 if !result.is_empty() {
119 break;
120 }
121 return Err(Error::from(error));
122 }
123 }
124 }
125 Ok(result)
126 }
127
128 fn parse_sync(&self, hvm: bool) -> Result<BootImageInfo> {
129 let elf = ElfBytes::<AnyEndian>::minimal_parse(self.data.as_slice())?;
130 let headers = elf
131 .section_headers()
132 .ok_or(Error::ElfInvalidImage("section headers missing"))?;
133 let mut xen_notes: HashMap<u64, ElfNoteValue> = HashMap::new();
134
135 for header in headers {
136 if header.sh_type != SHT_NOTE {
137 continue;
138 }
139
140 let notes = elf.section_data_as_notes(&header)?;
141 for note in notes {
142 let Note::Unknown(note) = note else {
143 continue;
144 };
145
146 if note.name == "Xen" {
147 for typ in XEN_ELFNOTE_TYPES {
148 if typ.id != note.n_type {
149 continue;
150 }
151
152 let value = if !typ.is_string {
153 xen_note_value_as_u64(elf.ehdr.endianness, note.desc).unwrap_or(0)
154 } else {
155 0
156 };
157
158 xen_notes.insert(typ.id, ElfNoteValue { value });
159 }
160 }
161 }
162 }
163
164 if xen_notes.is_empty() {
165 return Err(Error::ElfXenSupportMissing);
166 }
167
168 let paddr_offset = xen_notes
169 .get(&XEN_ELFNOTE_PADDR_OFFSET)
170 .ok_or(Error::ElfXenNoteMissing("PADDR_OFFSET"))?
171 .value;
172 let virt_base = xen_notes
173 .get(&XEN_ELFNOTE_VIRT_BASE)
174 .ok_or(Error::ElfXenNoteMissing("VIRT_BASE"))?
175 .value;
176 let entry = xen_notes
177 .get(&XEN_ELFNOTE_ENTRY)
178 .ok_or(Error::ElfXenNoteMissing("ENTRY"))?
179 .value;
180 let virt_hypercall = xen_notes
181 .get(&XEN_ELFNOTE_HYPERCALL_PAGE)
182 .ok_or(Error::ElfXenNoteMissing("HYPERCALL_PAGE"))?
183 .value;
184 let init_p2m = xen_notes
185 .get(&XEN_ELFNOTE_INIT_P2M)
186 .ok_or(Error::ElfXenNoteMissing("INIT_P2M"))?
187 .value;
188 let mod_start_pfn = xen_notes
189 .get(&XEN_ELFNOTE_MOD_START_PFN)
190 .ok_or(Error::ElfXenNoteMissing("MOD_START_PFN"))?
191 .value;
192
193 let phys32_entry = xen_notes.get(&XEN_ELFNOTE_PHYS32_ENTRY).map(|x| x.value);
194
195 let mut start: u64 = u64::MAX;
196 let mut end: u64 = 0;
197
198 let segments = elf
199 .segments()
200 .ok_or(Error::ElfInvalidImage("segments missing"))?;
201
202 for header in segments {
203 if (header.p_type != PT_LOAD) || (header.p_flags & (PF_R | PF_W | PF_X)) == 0 {
204 continue;
205 }
206 let paddr = header.p_paddr;
207 let memsz = header.p_memsz;
208 if start > paddr {
209 start = paddr;
210 }
211
212 if end < paddr + memsz {
213 end = paddr + memsz;
214 }
215 }
216
217 if paddr_offset != u64::MAX && virt_base == u64::MAX {
218 return Err(Error::ElfInvalidImage(
219 "paddr_offset specified, but virt_base is not specified",
220 ));
221 }
222
223 let virt_offset = virt_base - paddr_offset;
224 let virt_kstart = start + virt_offset;
225 let virt_kend = end + virt_offset;
226 let mut virt_entry = entry;
227 if hvm {
228 if let Some(entry) = phys32_entry {
229 virt_entry = entry;
230 } else {
231 virt_entry = elf.ehdr.e_entry;
232 }
233 }
234 let image_info = BootImageInfo {
235 start,
236 virt_base,
237 virt_kstart,
238 virt_kend,
239 virt_hypercall,
240 virt_entry,
241 virt_p2m_base: init_p2m,
242 unmapped_initrd: mod_start_pfn != 0,
243 };
244 Ok(image_info)
245 }
246
247 pub fn into_elf_bytes(self) -> Arc<Vec<u8>> {
248 self.data
249 }
250}
251
252#[derive(Debug)]
253struct ElfNoteValue {
254 value: u64,
255}
256
257#[async_trait::async_trait]
258impl BootImageLoader for ElfImageLoader {
259 async fn parse(&self, hvm: bool) -> Result<BootImageInfo> {
260 let loader = self.clone();
261 tokio::task::spawn_blocking(move || loader.parse_sync(hvm)).await?
262 }
263
264 async fn load(&self, image_info: &BootImageInfo, dst: &mut [u8]) -> Result<()> {
265 let elf = ElfBytes::<AnyEndian>::minimal_parse(self.data.as_slice())?;
266 let segments = elf
267 .segments()
268 .ok_or(Error::ElfInvalidImage("segments missing"))?;
269
270 debug!(
271 "load dst={:#x} segments={}",
272 dst.as_ptr() as u64,
273 segments.len()
274 );
275 for header in segments {
276 let paddr = header.p_paddr;
277 let filesz = header.p_filesz;
278 let memsz = header.p_memsz;
279 let base_offset = paddr - image_info.start;
280 let data = elf.segment_data(&header)?;
281 let segment_dst = &mut dst[base_offset as usize..];
282 let copy_slice = &data[0..filesz as usize];
283 debug!(
284 "load copy hdr={:?} dst={:#x} len={}",
285 header,
286 copy_slice.as_ptr() as u64,
287 copy_slice.len()
288 );
289 copy(segment_dst, copy_slice);
290 if (memsz - filesz) > 0 {
291 let remaining = &mut segment_dst[filesz as usize..memsz as usize];
292 debug!(
293 "load fill_zero hdr={:?} dst={:#x} len={}",
294 header.p_offset,
295 remaining.as_ptr() as u64,
296 remaining.len()
297 );
298 remaining.fill(0);
299 }
300 }
301 Ok(())
302 }
303}