1use crate::{PAGE_SIZE, level_4_entries::UsedLevel4Entries};
2use bootloader_api::{config::Mapping, info::TlsTemplate};
3use core::{cmp, iter::Step, mem::size_of, ops::Add};
4
5use x86_64::{
6 PhysAddr, VirtAddr, align_up,
7 structures::paging::{
8 FrameAllocator, Page, PageSize, PageTableFlags as Flags, PhysFrame, Size4KiB, Translate,
9 mapper::{MappedFrame, MapperAllSizes, TranslateResult},
10 },
11};
12use xmas_elf::{
13 ElfFile, dynamic, header,
14 program::{self, ProgramHeader, SegmentData, Type},
15 sections::Rela,
16};
17
18use super::Kernel;
19
20const COPIED: Flags = Flags::BIT_9;
22
23struct Loader<'a, M, F> {
24 elf_file: ElfFile<'a>,
25 inner: Inner<'a, M, F>,
26}
27
28struct Inner<'a, M, F> {
29 kernel_offset: PhysAddr,
30 virtual_address_offset: VirtualAddressOffset,
31 page_table: &'a mut M,
32 frame_allocator: &'a mut F,
33}
34
35impl<'a, M, F> Loader<'a, M, F>
36where
37 M: MapperAllSizes + Translate,
38 F: FrameAllocator<Size4KiB>,
39{
40 fn new(
41 kernel: Kernel<'a>,
42 page_table: &'a mut M,
43 frame_allocator: &'a mut F,
44 used_entries: &mut UsedLevel4Entries,
45 ) -> Result<Self, &'static str> {
46 log::info!("Elf file loaded at {:#p}", kernel.elf.input);
47 let kernel_offset = PhysAddr::new(&kernel.elf.input[0] as *const u8 as u64);
48 if !kernel_offset.is_aligned(PAGE_SIZE) {
49 return Err("Loaded kernel ELF file is not sufficiently aligned");
50 }
51
52 let elf_file = kernel.elf;
53 for program_header in elf_file.program_iter() {
54 program::sanity_check(program_header, &elf_file)?;
55 }
56
57 let virtual_address_offset = match elf_file.header.pt2.type_().as_type() {
58 header::Type::None => unimplemented!(),
59 header::Type::Relocatable => unimplemented!(),
60 header::Type::Executable => match kernel.config.mappings.kernel_base {
61 Mapping::Dynamic => VirtualAddressOffset::zero(),
62 _ => {
63 return Err(concat!(
64 "Invalid kernel_code mapping. ",
65 "Executable can only be mapped at virtual_address_offset 0."
66 ));
67 }
68 },
69 header::Type::SharedObject => {
70 let ElfMemoryRequirements {
71 size,
72 align,
73 min_addr,
74 } = calc_elf_memory_requirements(&elf_file);
75 match kernel.config.mappings.kernel_base {
76 Mapping::Dynamic => {
77 let offset = used_entries.get_free_address(size, align).as_u64();
78 VirtualAddressOffset::new(i128::from(offset) - i128::from(min_addr))
79 }
80 Mapping::FixedAddress(address) => {
81 VirtualAddressOffset::new(i128::from(address))
82 }
83 }
84 }
85 header::Type::Core => unimplemented!(),
86 header::Type::ProcessorSpecific(_) => unimplemented!(),
87 };
88 log::info!(
89 "virtual_address_offset: {:#x}",
90 virtual_address_offset.virtual_address_offset()
91 );
92
93 used_entries.mark_segments(elf_file.program_iter(), virtual_address_offset);
94
95 header::sanity_check(&elf_file)?;
96 let loader = Loader {
97 elf_file,
98 inner: Inner {
99 kernel_offset,
100 virtual_address_offset,
101 page_table,
102 frame_allocator,
103 },
104 };
105
106 Ok(loader)
107 }
108
109 fn load_segments(&mut self) -> Result<Option<TlsTemplate>, &'static str> {
110 let mut tls_template = None;
112 for program_header in self.elf_file.program_iter() {
113 match program_header.get_type()? {
114 Type::Load => self.inner.handle_load_segment(program_header)?,
115 Type::Tls => {
116 if tls_template.is_none() {
117 tls_template = Some(self.inner.handle_tls_segment(program_header)?);
118 } else {
119 return Err("multiple TLS segments not supported");
120 }
121 }
122 Type::Null
123 | Type::Dynamic
124 | Type::Interp
125 | Type::Note
126 | Type::ShLib
127 | Type::Phdr
128 | Type::GnuRelro
129 | Type::OsSpecific(_)
130 | Type::ProcessorSpecific(_) => {}
131 }
132 }
133
134 for program_header in self.elf_file.program_iter() {
136 if let Type::Dynamic = program_header.get_type()? {
137 self.inner
138 .handle_dynamic_segment(program_header, &self.elf_file)?
139 }
140 }
141
142 for program_header in self.elf_file.program_iter() {
145 if let Type::GnuRelro = program_header.get_type()? {
146 self.inner.handle_relro_segment(program_header);
147 }
148 }
149
150 self.inner.remove_copied_flags(&self.elf_file).unwrap();
151
152 Ok(tls_template)
153 }
154
155 fn entry_point(&self) -> VirtAddr {
156 VirtAddr::new(self.inner.virtual_address_offset + self.elf_file.header.pt2.entry_point())
157 }
158}
159
160impl<'a, M, F> Inner<'a, M, F>
161where
162 M: MapperAllSizes + Translate,
163 F: FrameAllocator<Size4KiB>,
164{
165 fn handle_load_segment(&mut self, segment: ProgramHeader) -> Result<(), &'static str> {
166 log::info!("Handling Segment: {:x?}", segment);
167
168 let phys_start_addr = self.kernel_offset + segment.offset();
169 let start_frame: PhysFrame = PhysFrame::containing_address(phys_start_addr);
170 let end_frame: PhysFrame =
171 PhysFrame::containing_address(phys_start_addr + segment.file_size() - 1u64);
172
173 let virt_start_addr = VirtAddr::new(self.virtual_address_offset + segment.virtual_addr());
174 let start_page: Page = Page::containing_address(virt_start_addr);
175
176 let mut segment_flags = Flags::PRESENT;
177 if !segment.flags().is_execute() {
178 segment_flags |= Flags::NO_EXECUTE;
179 }
180 if segment.flags().is_write() {
181 segment_flags |= Flags::WRITABLE;
182 }
183
184 for frame in PhysFrame::range_inclusive(start_frame, end_frame) {
186 let offset = frame - start_frame;
187 let page = start_page + offset;
188 let flusher = unsafe {
189 self.page_table
193 .map_to_with_table_flags(
194 page,
195 frame,
196 segment_flags,
197 Flags::PRESENT | Flags::WRITABLE,
198 self.frame_allocator,
199 )
200 .map_err(|_err| "map_to failed")?
201 };
202 flusher.ignore();
204 }
205
206 if segment.mem_size() > segment.file_size() {
208 self.handle_bss_section(&segment, segment_flags)?;
210 }
211
212 Ok(())
213 }
214
215 fn handle_bss_section(
216 &mut self,
217 segment: &ProgramHeader,
218 segment_flags: Flags,
219 ) -> Result<(), &'static str> {
220 log::info!("Mapping bss section");
221
222 let virt_start_addr = VirtAddr::new(self.virtual_address_offset + segment.virtual_addr());
223 let mem_size = segment.mem_size();
224 let file_size = segment.file_size();
225
226 let zero_start = virt_start_addr + file_size;
228 let zero_end = virt_start_addr + mem_size;
229
230 type PageArray = [u64; Size4KiB::SIZE as usize / 8];
232 const ZERO_ARRAY: PageArray = [0; Size4KiB::SIZE as usize / 8];
233
234 let data_bytes_before_zero = zero_start.as_u64() & 0xfff;
237 if data_bytes_before_zero != 0 {
238 let last_page = Page::containing_address(virt_start_addr + file_size - 1u64);
268 let new_frame = unsafe { self.make_mut(last_page) };
269 let new_bytes_ptr = new_frame.start_address().as_u64() as *mut u8;
270 unsafe {
271 core::ptr::write_bytes(
272 new_bytes_ptr.add(data_bytes_before_zero as usize),
273 0,
274 (Size4KiB::SIZE - data_bytes_before_zero) as usize,
275 );
276 }
277 }
278
279 let start_page: Page =
281 Page::containing_address(VirtAddr::new(align_up(zero_start.as_u64(), Size4KiB::SIZE)));
282 let end_page = Page::containing_address(zero_end - 1u64);
283 for page in Page::range_inclusive(start_page, end_page) {
284 let frame = self.frame_allocator.allocate_frame().unwrap();
286
287 let frame_ptr = frame.start_address().as_u64() as *mut PageArray;
289 unsafe { frame_ptr.write(ZERO_ARRAY) };
290
291 let flusher = unsafe {
293 self.page_table
297 .map_to_with_table_flags(
298 page,
299 frame,
300 segment_flags,
301 Flags::PRESENT | Flags::WRITABLE,
302 self.frame_allocator,
303 )
304 .map_err(|_err| "Failed to map new frame for bss memory")?
305 };
306 flusher.ignore();
308 }
309
310 Ok(())
311 }
312
313 fn copy_from(&self, addr: VirtAddr, buf: &mut [u8]) {
319 let end_inclusive_addr = Step::forward_checked(addr, buf.len() - 1)
324 .expect("end address outside of the virtual address space");
325 let start_page = Page::<Size4KiB>::containing_address(addr);
326 let end_inclusive_page = Page::<Size4KiB>::containing_address(end_inclusive_addr);
327
328 for page in start_page..=end_inclusive_page {
329 let phys_addr = self
331 .page_table
332 .translate_page(page)
333 .expect("address is not mapped to the kernel's memory space");
334
335 let page_start = page.start_address();
339 let page_end_inclusive = page.start_address() + 4095u64;
340
341 let start_copy_address = cmp::max(addr, page_start);
343 let end_inclusive_copy_address = cmp::min(end_inclusive_addr, page_end_inclusive);
344
345 let start_offset_in_frame = start_copy_address - page_start;
347 let end_inclusive_offset_in_frame = end_inclusive_copy_address - page_start;
348
349 let copy_len = end_inclusive_offset_in_frame - start_offset_in_frame + 1;
351
352 let start_phys_addr = phys_addr.start_address() + start_offset_in_frame;
354
355 let start_offset_in_buf = Step::steps_between(&addr, &start_copy_address).1.unwrap();
358
359 let src_ptr = start_phys_addr.as_u64() as *const u8;
362 let src = unsafe {
363 &*core::ptr::slice_from_raw_parts(src_ptr, copy_len as usize)
367 };
368
369 let dest = &mut buf[start_offset_in_buf..][..copy_len as usize];
371
372 dest.copy_from_slice(src);
374 }
375 }
376
377 unsafe fn copy_to(&mut self, addr: VirtAddr, buf: &[u8]) {
386 let end_inclusive_addr = Step::forward_checked(addr, buf.len() - 1)
391 .expect("the end address should be in the virtual address space");
392 let start_page = Page::<Size4KiB>::containing_address(addr);
393 let end_inclusive_page = Page::<Size4KiB>::containing_address(end_inclusive_addr);
394
395 for page in start_page..=end_inclusive_page {
396 let phys_addr = unsafe {
398 self.make_mut(page)
400 };
401
402 let page_start = page.start_address();
406 let page_end_inclusive = page.start_address() + 4095u64;
407
408 let start_copy_address = cmp::max(addr, page_start);
410 let end_inclusive_copy_address = cmp::min(end_inclusive_addr, page_end_inclusive);
411
412 let start_offset_in_frame = start_copy_address - page_start;
414 let end_inclusive_offset_in_frame = end_inclusive_copy_address - page_start;
415
416 let copy_len = end_inclusive_offset_in_frame - start_offset_in_frame + 1;
418
419 let start_phys_addr = phys_addr.start_address() + start_offset_in_frame;
421
422 let start_offset_in_buf = Step::steps_between(&addr, &start_copy_address).1.unwrap();
425
426 let dest_ptr = start_phys_addr.as_u64() as *mut u8;
429 let dest = unsafe {
430 &mut *core::ptr::slice_from_raw_parts_mut(dest_ptr, copy_len as usize)
434 };
435
436 let src = &buf[start_offset_in_buf..][..copy_len as usize];
438
439 dest.copy_from_slice(src);
441 }
442 }
443
444 unsafe fn make_mut(&mut self, page: Page) -> PhysFrame {
461 let (frame, flags) = match self.page_table.translate(page.start_address()) {
462 TranslateResult::Mapped {
463 frame,
464 offset: _,
465 flags,
466 } => (frame, flags),
467 TranslateResult::NotMapped => panic!("{:?} is not mapped", page),
468 TranslateResult::InvalidFrameAddress(_) => unreachable!(),
469 };
470 let frame = if let MappedFrame::Size4KiB(frame) = frame {
471 frame
472 } else {
473 unreachable!()
475 };
476
477 if flags.contains(COPIED) {
478 return frame;
480 }
481
482 let new_frame = self.frame_allocator.allocate_frame().unwrap();
484 let frame_ptr = frame.start_address().as_u64() as *const u8;
485 let new_frame_ptr = new_frame.start_address().as_u64() as *mut u8;
486 unsafe {
487 core::ptr::copy_nonoverlapping(frame_ptr, new_frame_ptr, Size4KiB::SIZE as usize);
488 }
489
490 self.page_table.unmap(page).unwrap().1.ignore();
492 let new_flags = flags | COPIED;
493 unsafe {
494 self.page_table
495 .map_to(page, new_frame, new_flags, self.frame_allocator)
496 .unwrap()
497 .ignore();
498 }
499
500 new_frame
501 }
502
503 fn remove_copied_flags(&mut self, elf_file: &ElfFile) -> Result<(), &'static str> {
505 for program_header in elf_file.program_iter() {
506 if let Type::Load = program_header.get_type()? {
507 if program_header.mem_size() == 0 {
508 continue;
509 }
510 let start = self.virtual_address_offset + program_header.virtual_addr();
511 let end = start + (program_header.mem_size() - 1);
512 let start = VirtAddr::new(start);
513 let end = VirtAddr::new(end);
514 let start_page = Page::containing_address(start);
515 let end_page = Page::containing_address(end);
516 for page in Page::<Size4KiB>::range_inclusive(start_page, end_page) {
517 let res = self.page_table.translate(page.start_address());
519 let flags = match res {
520 TranslateResult::Mapped {
521 frame: _,
522 offset: _,
523 flags,
524 } => flags,
525 TranslateResult::NotMapped | TranslateResult::InvalidFrameAddress(_) => {
526 unreachable!("has the elf file not been mapped correctly?")
527 }
528 };
529
530 if flags.contains(COPIED) {
531 unsafe {
533 self.page_table
534 .update_flags(page, flags & !COPIED)
535 .unwrap()
536 .ignore();
537 }
538 }
539 }
540 }
541 }
542 Ok(())
543 }
544
545 fn handle_tls_segment(&mut self, segment: ProgramHeader) -> Result<TlsTemplate, &'static str> {
546 Ok(TlsTemplate {
547 start_addr: self.virtual_address_offset + segment.virtual_addr(),
548 mem_size: segment.mem_size(),
549 file_size: segment.file_size(),
550 })
551 }
552
553 fn handle_dynamic_segment(
554 &mut self,
555 segment: ProgramHeader,
556 elf_file: &ElfFile,
557 ) -> Result<(), &'static str> {
558 let data = segment.get_data(elf_file)?;
559 let data = if let SegmentData::Dynamic64(data) = data {
560 data
561 } else {
562 panic!("expected Dynamic64 segment")
563 };
564
565 let mut rela = None;
567 let mut rela_size = None;
568 let mut rela_ent = None;
569 for rel in data {
570 let tag = rel.get_tag()?;
571 match tag {
572 dynamic::Tag::Rela => {
573 let ptr = rel.get_ptr()?;
574 let prev = rela.replace(ptr);
575 if prev.is_some() {
576 return Err("Dynamic section contains more than one Rela entry");
577 }
578 }
579 dynamic::Tag::RelaSize => {
580 let val = rel.get_val()?;
581 let prev = rela_size.replace(val);
582 if prev.is_some() {
583 return Err("Dynamic section contains more than one RelaSize entry");
584 }
585 }
586 dynamic::Tag::RelaEnt => {
587 let val = rel.get_val()?;
588 let prev = rela_ent.replace(val);
589 if prev.is_some() {
590 return Err("Dynamic section contains more than one RelaEnt entry");
591 }
592 }
593 _ => {}
594 }
595 }
596 let offset = if let Some(rela) = rela {
597 rela
598 } else {
599 if rela_size.is_some() || rela_ent.is_some() {
602 return Err("Rela entry is missing but RelaSize or RelaEnt have been provided");
603 }
604
605 return Ok(());
606 };
607 let total_size = rela_size.ok_or("RelaSize entry is missing")?;
608 let entry_size = rela_ent.ok_or("RelaEnt entry is missing")?;
609
610 assert_eq!(
612 entry_size,
613 size_of::<Rela<u64>>() as u64,
614 "unsupported entry size: {entry_size}"
615 );
616
617 let num_entries = total_size / entry_size;
619 for idx in 0..num_entries {
620 let rela = self.read_relocation(offset, idx);
621 self.apply_relocation(rela, elf_file)?;
622 }
623
624 Ok(())
625 }
626
627 fn read_relocation(&self, relocation_table: u64, idx: u64) -> Rela<u64> {
629 let offset = relocation_table + size_of::<Rela<u64>>() as u64 * idx;
631 let value = self.virtual_address_offset + offset;
632 let addr = VirtAddr::try_new(value).expect("relocation table is outside the address space");
633
634 let mut buf = [0; 24];
636 self.copy_from(addr, &mut buf);
637
638 unsafe {
640 core::ptr::read_unaligned(&buf as *const u8 as *const Rela<u64>)
643 }
644 }
645
646 fn apply_relocation(
647 &mut self,
648 rela: Rela<u64>,
649 elf_file: &ElfFile,
650 ) -> Result<(), &'static str> {
651 let symbol_idx = rela.get_symbol_table_index();
652 assert_eq!(
653 symbol_idx, 0,
654 "relocations using the symbol table are not supported"
655 );
656
657 match rela.get_type() {
658 8 => {
660 check_is_in_load(elf_file, rela.get_offset())?;
663
664 let addr = self.virtual_address_offset + rela.get_offset();
666 let addr = VirtAddr::new(addr);
667
668 let value = self.virtual_address_offset + rela.get_addend();
670
671 unsafe {
673 self.copy_to(addr, &value.to_ne_bytes());
675 }
676 }
677 ty => unimplemented!("relocation type {:x} not supported", ty),
678 }
679
680 Ok(())
681 }
682
683 fn handle_relro_segment(&mut self, program_header: ProgramHeader) {
689 let start = self.virtual_address_offset + program_header.virtual_addr();
690 let end = start + program_header.mem_size();
691 let start = VirtAddr::new(start);
692 let end = VirtAddr::new(end);
693 let start_page = Page::containing_address(start);
694 let end_page = Page::containing_address(end - 1u64);
695 for page in Page::<Size4KiB>::range_inclusive(start_page, end_page) {
696 let res = self.page_table.translate(page.start_address());
698 let flags = match res {
699 TranslateResult::Mapped {
700 frame: _,
701 offset: _,
702 flags,
703 } => flags,
704 TranslateResult::NotMapped | TranslateResult::InvalidFrameAddress(_) => {
705 unreachable!("has the elf file not been mapped correctly?")
706 }
707 };
708
709 if flags.contains(Flags::WRITABLE) {
710 unsafe {
712 self.page_table
713 .update_flags(page, flags & !Flags::WRITABLE)
714 .unwrap()
715 .ignore();
716 }
717 }
718 }
719 }
720}
721
722fn check_is_in_load(elf_file: &ElfFile, virt_offset: u64) -> Result<(), &'static str> {
724 for program_header in elf_file.program_iter() {
725 if let Type::Load = program_header.get_type()? {
726 if program_header.virtual_addr() <= virt_offset {
727 let offset_in_segment = virt_offset - program_header.virtual_addr();
728 if offset_in_segment < program_header.mem_size() {
729 return Ok(());
730 }
731 }
732 }
733 }
734 Err("offset is not in load segment")
735}
736
737pub fn load_kernel(
742 kernel: Kernel<'_>,
743 page_table: &mut (impl MapperAllSizes + Translate),
744 frame_allocator: &mut impl FrameAllocator<Size4KiB>,
745 used_entries: &mut UsedLevel4Entries,
746) -> Result<(VirtAddr, VirtAddr, Option<TlsTemplate>), &'static str> {
747 let mut loader = Loader::new(kernel, page_table, frame_allocator, used_entries)?;
748 let tls_template = loader.load_segments()?;
749
750 Ok((
751 VirtAddr::new(loader.inner.virtual_address_offset.virtual_address_offset() as u64),
752 loader.entry_point(),
753 tls_template,
754 ))
755}
756
757pub struct ElfMemoryRequirements {
759 pub size: u64,
761 pub align: u64,
763 pub min_addr: u64,
765}
766
767pub fn calc_elf_memory_requirements(elf_file: &ElfFile) -> ElfMemoryRequirements {
769 let load_program_headers = elf_file
771 .program_iter()
772 .filter(|h| matches!(h.get_type(), Ok(Type::Load)));
773 let max_addr = load_program_headers
774 .clone()
775 .map(|h| h.virtual_addr() + h.mem_size())
776 .max()
777 .unwrap_or(0);
778 let min_addr = load_program_headers
779 .clone()
780 .map(|h| h.virtual_addr())
781 .min()
782 .unwrap_or(0);
783 let size = max_addr - min_addr;
784 let align = load_program_headers.map(|h| h.align()).max().unwrap_or(1);
785 ElfMemoryRequirements {
786 size,
787 align,
788 min_addr,
789 }
790}
791
792#[derive(Clone, Copy)]
795pub struct VirtualAddressOffset {
796 virtual_address_offset: i128,
797}
798
799impl VirtualAddressOffset {
800 pub fn zero() -> Self {
801 Self::new(0)
802 }
803
804 pub fn new(virtual_address_offset: i128) -> Self {
805 Self {
806 virtual_address_offset,
807 }
808 }
809
810 pub fn virtual_address_offset(&self) -> i128 {
811 self.virtual_address_offset
812 }
813}
814
815impl Add<u64> for VirtualAddressOffset {
816 type Output = u64;
817
818 fn add(self, offset: u64) -> Self::Output {
819 u64::try_from(
820 self.virtual_address_offset
821 .checked_add(i128::from(offset))
822 .unwrap(),
823 )
824 .unwrap()
825 }
826}