elfloader: band-aid handling of PHDRs that span flash pages

- inspect the PHDR p_type field without copying the entire header
  when checking if a PHDR is loadable (we assume the PHDR is
  word-aligned which is probably ok)
- advance to a new flash page--as needed--when reading a PHDR
- add an assert with an informative message when checking for a
  PHDR that spans flash pages; previously this would trigger an
  obscure assert when constructing the slice of raw bytes
- add Elf32Phdr::get_type() to return just the p_type field of a PHDR
- change Elf32Phdr::from_bytes() to take an immutable ref

This hacks around problems where the latest CHERIoT lld appends PHDRs
that require multiple flash pages but are not loaded. A better fix would
assemble PHDR's that span flash pages.

Change-Id: I1cfdc93ecb79fa7008bdfcab8a9210fbaaf89baf
diff --git a/capsules/src/elfloader_capsule.rs b/capsules/src/elfloader_capsule.rs
index af19523..0e80753 100644
--- a/capsules/src/elfloader_capsule.rs
+++ b/capsules/src/elfloader_capsule.rs
@@ -74,7 +74,7 @@
     LoadElfHeader(u32 /* cursor */),
     LoadProgramHeaderTable(
         u32, /* cursor */
-        u32, /* offset */
+        usize, /* offset */
         u16, /* num */
         u16, /* index */
     ),
@@ -336,7 +336,7 @@
                     // Kick off reading the ELF program headers.
                     self.state.set(ElfLoaderState::LoadProgramHeaderTable(
                         cursor,
-                        elf_header.phoff(),
+                        elf_header.phoff() as usize,
                         elf_header.phnum(),
                         0,
                     ));
@@ -360,14 +360,15 @@
         self.read_page.map(|page| {
             let mut_page = page.as_mut();
             match self.state.get() {
-                ElfLoaderState::LoadProgramHeaderTable(_, offset, _, index) => {
-                    let phdr = Elf32Phdr::from_bytes(
-                        &mut mut_page[(((offset as usize)
-                            + (index as usize * core::mem::size_of::<Elf32Phdr>()))
-                            as usize)..],
-                    );
+                ElfLoaderState::LoadProgramHeaderTable(_, page_offset, _, _index) => {
+                    let page_bytes = &mut_page[page_offset..];
+                    // NB: assume word-alignment so it's safe to read the p_type field
+                    if Elf32Phdr::get_type(page_bytes) == PT_LOAD {
+                        // NB: we do not handle a PHDR spanning flash pages
+                        assert!(page_offset + core::mem::size_of::<Elf32Phdr>() <= self.page_len as usize,
+                                "ELF PHDR spans flash pages");
+                        let phdr = Elf32Phdr::from_bytes(page_bytes);
 //dprintf!("[{}] {:#08x?}\r\n", index, &phdr);
-                    if phdr.p_type == PT_LOAD {
                         let mut sel4_state = self.sel4_state.get();
                         let mut next_slot: Option<usize> = None;
                         for i in 0..sel4_state.phdrs.len() {
@@ -388,15 +389,23 @@
             };
         });
         match self.state.get() {
-            ElfLoaderState::LoadProgramHeaderTable(cursor, offset, count, index) => {
+            ElfLoaderState::LoadProgramHeaderTable(cursor, page_offset, count, index) => {
                 if index + 1 < count {
+                    let mut new_page_offset = page_offset + core::mem::size_of::<Elf32Phdr>();
+                    let new_cursor = if new_page_offset >= self.page_len as usize {
+                        new_page_offset %= self.page_len as usize;
+                        cursor + self.page_len
+                    } else {
+                        cursor
+                    };
                     self.state.set(ElfLoaderState::LoadProgramHeaderTable(
-                        cursor,
-                        offset,
+                        new_cursor,
+                        new_page_offset,
                         count,
                         index + 1,
                     ));
-                    self.read_page(cursor);
+                    // NB: this may read the same flash page for multiple PHDRs
+                    self.read_page(new_cursor);
                 } else {
                     // Record state derived from phdrs before they get clobbered.
                     let mut segment_load_offset = 0;
diff --git a/utils/src/elf_loader.rs b/utils/src/elf_loader.rs
index 3055828..288a79a 100644
--- a/utils/src/elf_loader.rs
+++ b/utils/src/elf_loader.rs
@@ -59,7 +59,10 @@
 }
 
 impl Elf32Phdr {
-    pub fn from_bytes(b: &mut [u8]) -> Elf32Phdr {
+    pub fn get_type(b: &[u8]) -> u32 {
+        unsafe { ptr::read(b.as_ptr() as *const u32) }
+    }
+    pub fn from_bytes(b: &[u8]) -> Elf32Phdr {
         unsafe { ptr::read(b.as_ptr() as *const Elf32Phdr) }
     }
 }