xenplatform/
elfloader.rs

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}