elfloader: major overhaul to support non-seL4 images

Overhaul the ELF loader support to handle loading ELF images unlike seL4
(e.g. many ELF load segments, no capdl-loader) and cleanup some code.
With these changes CHERIoT firmware images load.

- add support for loading multiple ELF load segments
- handle a missing capdl-loader; e.g. when starting the SMC do not
  post the boot message to the mailbox
- improve loading unaligned ELF segments; still does not handle all cases
  but it's unclear if they matter (will panic)
- handle running out of space for ELF phdrs more gracefully
- change SEL4State to have only one instance of "offset" & "headers"
  to save memory, especially when we grow the "headers" array to handle
  more complex ELF images
- combine the FindFile, LoadElfHeaders, and LoadElf tasks into a
  single LoadElf task; this better safeguards SEL4State that was shared
  between the tasks and clobbered if the old tasks were mis-ordered
- reduce the task array size after combining FindFile & co
- add a Noop task for initializing/padding the task array
- cleanup SEL4State handling; e.g. move calculations derived from the
  ELF phdrs to immediately after the full set of headers are read;
  this safeguards against clobbering shared state between task phases
- change ELF segment log msgs to include the segment # and to only
  log non-zero size segments (file-data, bss)
- use the tar magic marker to identify EOF (when present)
- remove a bunch of dead code
- replace panic(<line-number>) by unreachable! calls since the line
  numbers were (wrong and) unmtaintable
- increase the tock app memory layout for the changes (we were right
  on the edge)

Fix: 331955054
Change-Id: Iecb00e5545c9cf5e603d49e3f2c7669ed36581e3
diff --git a/app/layout_matcha.ld b/app/layout_matcha.ld
index 57e2378..906aaaa 100644
--- a/app/layout_matcha.ld
+++ b/app/layout_matcha.ld
@@ -10,7 +10,7 @@
    * the kernel binary, check for the actual address of APP_MEMORY!
    */
   FLASH (rx) : ORIGIN = 0x20080040, LENGTH = 0x80000 - 0x40
-  SRAM (rwx) : ORIGIN = 0x10006800, LENGTH = 0x19800
+  SRAM (rwx) : ORIGIN = 0x10007800, LENGTH = 0x18800
 }
 
 MPU_MIN_ALIGN = 1K;
diff --git a/capsules/src/elfloader_capsule.rs b/capsules/src/elfloader_capsule.rs
index 22ec75a..af19523 100644
--- a/capsules/src/elfloader_capsule.rs
+++ b/capsules/src/elfloader_capsule.rs
@@ -12,9 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! Trivial Shodan elf loader capsule
-
-// TODO(sleffler): cleanup panic msgs (currently fuzzy line numbers)
+//! Shodan ELF loader capsule
 
 use core::cell::Cell;
 use core::marker::PhantomData;
@@ -30,6 +28,7 @@
 use matcha_utils::tar_loader::TarHeader;
 
 const PT_LOAD: u32 = 1;  // Loadable segment
+const INVALID_LOAD_OFFSET: u32 = 0;
 
 pub struct ElfLoaderCapsule<'a, F: hil::flash::Flash + 'static> {
     mailbox_hal: Option<&'static dyn MailboxHAL>,
@@ -40,48 +39,32 @@
     page_len: u32,
     state: Cell<ElfLoaderState>,
     phantom: PhantomData<&'a ()>,
-    tasks: Cell<[(LoadTasks, bool); 7]>,
+    tasks: Cell<[(LoadTasks, bool); 3]>,
     current_task: OptionalCell<LoadTasks>,
     sel4_state: Cell<SEL4State>,
 }
 
 #[derive(Clone, Copy, PartialEq)]
 enum LoadTasks {
-    FindFile(&'static str /* name */),
-    LoadElfHeaders(&'static str /* name */),
+    Noop,
     LoadElf(&'static str /* name */),
     StartSmc,
 }
 
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Default)]
 struct SEL4State {
-    kernel_offset: u32,
-    capdl_loader_offset: u32,
-    kernel_entry_point: u32,
-    capdl_loader_entry_point: u32,
-    kernel_headers: [Option<Elf32Phdr>; 4],
-    capdl_loader_headers: [Option<Elf32Phdr>; 4],
-    kernel_bss_start: u32,
-    kernel_bss_size: u32,
-    capdl_bss_start: u32,
-    capdl_bss_size: u32,
-}
+    // State for current file being loaded.
+    load_offset: u32, // File offset
+    phdrs: [Option<Elf32Phdr>; 20], // ELF phdrs
 
-impl Default for SEL4State {
-    fn default() -> SEL4State {
-        SEL4State {
-            kernel_offset: 0,
-            capdl_loader_offset: 0,
-            kernel_entry_point: 0,
-            capdl_loader_entry_point: 0,
-            kernel_headers: [None, None, None, None],
-            capdl_loader_headers: [None, None, None, None],
-            kernel_bss_start: 0,
-            kernel_bss_size: 0,
-            capdl_bss_start: 0,
-            capdl_bss_size: 0,
-        }
-    }
+    // Saved state for the "kernel" image.
+    kernel_entry_point: u32,
+    kernel_phys_max: u32,
+
+    // Saved state for the "capdl-loader" image.
+    capdl_loader_entry_point: u32,
+    capdl_phys_min: u32,
+    capdl_phys_max: u32,
 }
 
 #[derive(Clone, Copy, PartialEq)]
@@ -96,7 +79,6 @@
         u16, /* index */
     ),
     LoadSegmentsNew(
-        &'static str, /* name */
         Elf32Phdr,    /* phdr */
         usize,        /* index */
         u32,          /* cursor */
@@ -106,6 +88,33 @@
     ),
 }
 
+fn print_segment(index: usize, cursor: u32, offset: u32, phdr: &Elf32Phdr) {
+    if phdr.p_filesz > 0 {
+        dprintf!(
+            "seg {:2}:{:X} -> {:X}:{:X} ({} bytes)\r\n",
+            index,
+            cursor,
+            phdr.p_paddr + offset,
+            phdr.p_paddr + offset + phdr.p_filesz,
+            { phdr.p_filesz },
+        );
+    }
+    let bss_size = phdr.p_memsz - phdr.p_filesz;
+    if bss_size > 0 {
+        let bss_start = phdr.p_paddr + offset + phdr.p_filesz;
+        let bss_end = bss_start + bss_size;
+
+        dprintf!(
+            "bss {:2}:{:X} -> {:X}:{:X} ({} bytes)\r\n",
+            index,
+            cursor,
+            bss_start,
+            bss_end,
+            bss_size
+        );
+    }
+}
+
 impl<'a, F: hil::flash::Flash> ElfLoaderCapsule<'a, F> {
     pub fn new() -> Self {
         Self {
@@ -117,15 +126,7 @@
             page_len: 0,
             state: Cell::new(ElfLoaderState::Idle),
             phantom: PhantomData,
-            tasks: Cell::new([
-                (LoadTasks::FindFile("kernel"), false),
-                (LoadTasks::FindFile("capdl-loader"), false),
-                (LoadTasks::LoadElfHeaders("kernel"), false),
-                (LoadTasks::LoadElfHeaders("capdl-loader"), false),
-                (LoadTasks::LoadElf("kernel"), false),
-                (LoadTasks::LoadElf("capdl-loader"), false),
-                (LoadTasks::StartSmc, false),
-            ]),
+            tasks: Cell::new([(LoadTasks::Noop, true); 3]),
             current_task: OptionalCell::empty(),
             sel4_state: Cell::new(SEL4State::default()),
         }
@@ -173,210 +174,166 @@
                 loop {}
             },
             |task| match task {
-                LoadTasks::FindFile(file) => {
+                LoadTasks::LoadElf(file) => {
                     self.find_file(file);
                 }
-                LoadTasks::LoadElfHeaders(file) => {
-                    self.load_elf_headers(file);
-                }
-                LoadTasks::LoadElf("kernel") => {
-                    dprintf!("Loading seL4 kernel\r\n");
-                    self.load_elf("kernel", 0);
-                }
-                LoadTasks::LoadElf("capdl-loader") => {
-                    dprintf!("Loading capdl-loader to the page after seL4\r\n");
-                    let sel4_pend =
-                        matcha_utils::round_up_to_page(matcha_utils::elf_loader::elf_phys_max_opt(
-                            &self.sel4_state.get().kernel_headers,
-                        ));
-                    let offset = sel4_pend
-                        - matcha_utils::elf_loader::elf_phys_min_opt(
-                            &self.sel4_state.get().capdl_loader_headers,
-                        );
-                    self.load_elf("capdl-loader", offset);
-                }
                 LoadTasks::StartSmc => {
-                    // NB: ui_p_reg_start & co. come from the code in seL4
-                    //    the processes these values.
-                    let ui_p_reg_start =
-                        matcha_utils::round_up_to_page(matcha_utils::elf_loader::elf_phys_max_opt(
-                            &self.sel4_state.get().kernel_headers,
-                        ));
-                    let ui_p_reg_end = ui_p_reg_start
-                        + matcha_utils::round_up_to_page(
-                            matcha_utils::elf_loader::elf_phys_max_opt(
-                                &self.sel4_state.get().capdl_loader_headers,
-                            ) - matcha_utils::elf_loader::elf_phys_min_opt(
-                                &self.sel4_state.get().capdl_loader_headers,
-                            ),
+                    let sel4_state = self.sel4_state.get();
+                    if sel4_state.capdl_phys_max > 0 {
+                        // If capdl-loader was loaded we are running seL4 and
+                        // it will expect a boot message to be waiting in the
+                        // mailbox FIFO telling it how to setup the kernel and
+                        // the rootserver.
+
+                        // NB: ui_p_reg_start & co. come from the code in seL4
+                        //   that processes these values.
+                        let ui_p_reg_start =
+                            matcha_utils::round_up_to_page(sel4_state.kernel_phys_max);
+                        let ui_p_reg_end = ui_p_reg_start
+                            + matcha_utils::round_up_to_page(
+                                sel4_state.capdl_phys_max - sel4_state.capdl_phys_min
+                            );
+                        let pv_offset = ui_p_reg_start - sel4_state.capdl_phys_min;
+                        let v_entry = sel4_state.capdl_loader_entry_point;
+    /*
+                        dprintf!(
+                            "StartSmc: ui_p_reg {:X}:{:X} pv_offset {:X} v_entry {:X}\r\n",
+                            ui_p_reg_start,
+                            ui_p_reg_end,
+                            pv_offset,
+                            v_entry
                         );
-                    let pv_offset = ui_p_reg_start
-                        - matcha_utils::elf_loader::elf_phys_min_opt(
-                            &self.sel4_state.get().capdl_loader_headers,
-                        );
-                    let v_entry = self.sel4_state.get().capdl_loader_entry_point;
-/*
-                    dprintf!(
-                        "{:X} {:X} {:X} {:X}\r\n",
-                        ui_p_reg_start,
-                        ui_p_reg_end,
-                        pv_offset,
-                        v_entry
-                    );
-                    dprintf!(
-                        "{:X} {:X} {:X}\r\n",
-                        ui_p_reg_start,
-                        matcha_utils::elf_loader::elf_phys_max_opt(
-                            &self.sel4_state.get().capdl_loader_headers
-                        ),
-                        matcha_utils::elf_loader::elf_phys_min_opt(
-                            &self.sel4_state.get().capdl_loader_headers
-                        ),
-                    );
-*/
-                    matcha_utils::smc_ram_zero(
-                        self.sel4_state.get().kernel_bss_start,
-                        self.sel4_state.get().kernel_bss_size as usize
-                    );
-                    matcha_utils::smc_ram_zero(
-                        self.sel4_state.get().capdl_bss_start,
-                        self.sel4_state.get().capdl_bss_size as usize
-                    );
-                    match self.smc_ctrl_hal {
-                        Some(smc_ctrl) => {
-                            self.mailbox_hal.map(|mb| {
-                                matcha_utils::smc_send_bootmsg(mb, [
-                                      ui_p_reg_start,
-                                      ui_p_reg_end,
-                                      pv_offset,
-                                      v_entry,
-                                ])
-                            });
-                            smc_ctrl.smc_ctrl_start();
-                        }
-                        None => {
-                            panic!("221");
-                        }
+    */
+                        // XXX suppress if not seL4?
+                        self.mailbox_hal.map(|mb| {
+                            matcha_utils::smc_send_bootmsg(mb, [
+                                  ui_p_reg_start,
+                                  ui_p_reg_end,
+                                  pv_offset,
+                                  v_entry,
+                            ])
+                        });
                     }
+
+                    assert!(self.smc_ctrl_hal.is_some());
+                    self.smc_ctrl_hal.unwrap().smc_ctrl_start();
                 }
-                _ => panic!("225"),
+                _ => unreachable!(),
             },
         );
     }
 
+    // Load seL4 state machine.
     pub fn load_sel4(&self) {
-        // Reset task list to start over.
-        let mut tasks = self.tasks.get();
-        for i in 0..tasks.len() {
-            tasks[i].1 = false;
-        }
-        self.tasks.replace(tasks);
+        self.tasks.replace([
+            (LoadTasks::LoadElf("kernel"), false),
+            (LoadTasks::LoadElf("capdl-loader"), false),
+            (LoadTasks::StartSmc, false),
+        ]);
         self.run_next_task();
     }
 
-    fn load_elf_headers(&self, name: &'static str) {
-        match name {
-            "kernel" => self.state.set(ElfLoaderState::LoadElfHeader(
-                self.sel4_state.get().kernel_offset,
-            )),
-            "capdl-loader" => self.state.set(ElfLoaderState::LoadElfHeader(
-                self.sel4_state.get().capdl_loader_offset,
-            )),
-            _ => panic!("207"),
-        }
-        match self.state.get() {
-            ElfLoaderState::LoadElfHeader(cursor) => self.read_page(cursor),
-            _ => panic!("214"),
-        }
+    // Reads the data at flash address |page|. |read_complete| (below)
+    // dispatchs the callback depending on the current ElfLoaderState.
+    fn read_page(&self, page: u32) {
+        self.flash.map(|flash| {
+            self.read_page.take().map(|read_page| {
+                self.flash_busy.set(true);
+                if let Err((_, buf)) = flash.read_page((page / self.page_len) as usize, read_page) {
+                    self.read_page.replace(buf);
+                }
+            });
+        });
     }
 
-    fn load_elf(&self, name: &'static str, offset: u32) {
-        let phdr = match name {
-            "kernel" => self.sel4_state.get().kernel_headers[0].unwrap(),
-            "capdl-loader" => self.sel4_state.get().capdl_loader_headers[0].unwrap(),
-            _ => panic!("290"),
-        };
-        let mod_offset = phdr.p_offset % self.page_len;
-        let div_offset = phdr.p_offset / self.page_len;
-        let cursor = match name {
-            "kernel" => self.sel4_state.get().kernel_offset,
-            "capdl-loader" => self.sel4_state.get().capdl_loader_offset,
-            _ => panic!("295"),
-        } + (div_offset * self.page_len);
-        let new_state =
-            ElfLoaderState::LoadSegmentsNew(name, phdr, 0, cursor, offset, mod_offset, 0);
-        self.state.set(new_state);
-        if phdr.p_type == PT_LOAD && phdr.p_filesz != 0 {
-            dprintf!(
-                "seg 0:{:X} -> {:X}:{:X} ({} bytes)\r\n",
-                cursor,
-                phdr.p_paddr + offset,
-                phdr.p_paddr + offset + phdr.p_filesz,
-                { phdr.p_filesz },
-            );
-            let bss_size = phdr.p_memsz - phdr.p_filesz;
-            let bss_start = phdr.p_paddr + offset + phdr.p_filesz;
-            let bss_end = bss_start + bss_size;
-
-            dprintf!(
-                "bss 0:{:X} -> {:X}:{:X} ({} bytes)\r\n",
-                cursor,
-                bss_start,
-                bss_end,
-                bss_size
-            );
-            match name {
-                "kernel" => {
-                    let mut sel4_state = self.sel4_state.get();
-                    sel4_state.kernel_bss_start = bss_start;
-                    sel4_state.kernel_bss_size = bss_size;
-                    self.sel4_state.replace(sel4_state);
-                },
-                "capdl-loader" => {
-                    let mut sel4_state = self.sel4_state.get();
-                    sel4_state.capdl_bss_start = bss_start;
-                    sel4_state.capdl_bss_size = bss_size;
-                    self.sel4_state.replace(sel4_state);
-                },
-                _ => panic!("349"),
-            };
-            // matcha_utils::smc_ram_zero(bss_start, bss_size as usize);
-            self.read_page(cursor);
-        } else {
-            panic!("295");
-        }
-    }
-
+    // Initiates a file lookup.
     fn find_file(&self, name: &'static str) {
         self.state.set(ElfLoaderState::FindingFile(name, 0));
         self.read_page(0);
     }
 
+    // Callback from |read_complete| for a file lookup. If the file was
+    // found start loading the ELF headers; otherwise advance the file
+    // lookup mechanism (to stride through the tar file). If the search
+    // was unsuccessful log to the console and advance to the next task.
+    fn find_file_callback(&self) {
+        self.read_page.map(|page| {
+            let mut_page = page.as_mut();
+            let tar_header = TarHeader::from_bytes(mut_page);
+            match self.state.get() {
+                ElfLoaderState::FindingFile(name, cursor) => {
+                    if tar_header.name().contains(name) {
+                        let mut sel4_state = self.sel4_state.get();
+                        sel4_state.load_offset = cursor + self.page_len;
+                        self.sel4_state.replace(sel4_state);
+                        self.state.set(ElfLoaderState::Idle);
+                    } else if !tar_header.has_magic() {
+                        // File not found.
+                        dprintf!("{} not found!\r\n", name);
+                        let mut sel4_state = self.sel4_state.get();
+                        sel4_state.load_offset = INVALID_LOAD_OFFSET;
+                        self.sel4_state.replace(sel4_state);
+                        self.state.set(ElfLoaderState::Idle);
+                    } else {
+                        let new_cursor =
+                            self.page_len + cursor + ((tar_header.size() + 511) & !511);
+                        self.state
+                            .set(ElfLoaderState::FindingFile(name, new_cursor));
+                    }
+                }
+                _ => unreachable!(),
+            };
+        });
+
+        match self.state.get() {
+            ElfLoaderState::FindingFile(_, cursor) => self.read_page(cursor),
+            ElfLoaderState::Idle => {
+                if self.sel4_state.get().load_offset != INVALID_LOAD_OFFSET {
+                    self.load_elf_headers();
+                } else {
+                    self.run_next_task();
+                }
+            }
+            _ => panic!("507"),
+        }
+    }
+
+    // Initiates reading ELF header data for the current file.
+    // NB: this clobbers program headers so any state derived from a file's
+    //   headers must be recorded below (see |load_elf_header_callback|).
+    fn load_elf_headers(&self) {
+        let mut sel4_state = self.sel4_state.get();
+        sel4_state.phdrs = SEL4State::default().phdrs; // Reset shared phdrs
+        let cursor = sel4_state.load_offset;
+        assert!(cursor  != INVALID_LOAD_OFFSET);
+        self.sel4_state.replace(sel4_state);
+        self.state.set(ElfLoaderState::LoadElfHeader(cursor));
+        self.read_page(cursor);
+    }
+
+    // Callback from |read_complete| after reading the ELF header. Saves
+    // the ELF entry point and initiate reading the ELF program headers.
     fn load_elf_header_callback(&self) {
         self.read_page.map(|page| {
             let mut_page = page.as_mut();
             let elf_header = Elf32Header::from_bytes(mut_page);
-            if !elf_header.check_magic() {
-                dprintf!("bad elf magic\r\n");
-                panic!("233");
-            }
+            assert!(elf_header.check_magic());
             self.current_task.map(|task| {
+                let mut sel4_state = self.sel4_state.get();
                 match task {
-                    LoadTasks::LoadElfHeaders("kernel") => {
-                        let mut sel4_state = self.sel4_state.get();
+                    LoadTasks::LoadElf("kernel") => {
                         sel4_state.kernel_entry_point = elf_header.e_entry;
-                        self.sel4_state.replace(sel4_state);
                     }
-                    LoadTasks::LoadElfHeaders("capdl-loader") => {
-                        let mut sel4_state = self.sel4_state.get();
+                    LoadTasks::LoadElf("capdl-loader") => {
                         sel4_state.capdl_loader_entry_point = elf_header.e_entry;
-                        self.sel4_state.replace(sel4_state);
                     }
-                    _ => panic!("290"),
+                    _ => unreachable!(),
                 };
+                self.sel4_state.replace(sel4_state);
             });
             match self.state.get() {
                 ElfLoaderState::LoadElfHeader(cursor) => {
+                    // Kick off reading the ELF program headers.
                     self.state.set(ElfLoaderState::LoadProgramHeaderTable(
                         cursor,
                         elf_header.phoff(),
@@ -384,16 +341,21 @@
                         0,
                     ));
                 }
-                _ => panic!("245"),
+                _ => unreachable!(),
             }
         });
         match self.state.get() {
             ElfLoaderState::LoadProgramHeaderTable(cursor, ..) =>
                 self.read_page(cursor),
-            _ => panic!("254"),
+            _ => unreachable!(),
         }
     }
 
+    // Callback from |read_complete| after reading an ELF program header.
+    // Saves the header and initiates a read of the next header or, if all
+    // headers have been read, initiates loading the ELF segments. If all
+    // headers have been read this also records per-file data based on the
+    // full set of headers (that may be clobbered if another file is read).
     fn load_program_header_table_callback(&self) {
         self.read_page.map(|page| {
             let mut_page = page.as_mut();
@@ -404,51 +366,25 @@
                             + (index as usize * core::mem::size_of::<Elf32Phdr>()))
                             as usize)..],
                     );
-                    if phdr.p_type == PT_LOAD
-                    /* && (phdr.p_filesz != 0)*/
-                    {
-                        self.current_task.map(|task| {
-                            match task {
-                                LoadTasks::LoadElfHeaders("kernel") => {
-                                    let mut sel4_state = self.sel4_state.get();
-                                    let mut next_slot: Option<usize> = None;
-                                    for i in 0..sel4_state.kernel_headers.len() {
-                                        if sel4_state.kernel_headers[i] == None {
-                                            next_slot = Some(i);
-                                            break;
-                                        }
-                                    }
-                                    match next_slot {
-                                        Some(slot) => {
-                                            sel4_state.kernel_headers[slot] = Some(phdr);
-                                        }
-                                        None => panic!("380"),
-                                    }
-                                    self.sel4_state.replace(sel4_state);
-                                }
-                                LoadTasks::LoadElfHeaders("capdl-loader") => {
-                                    let mut sel4_state = self.sel4_state.get();
-                                    let mut next_slot: Option<usize> = None;
-                                    for i in 0..sel4_state.capdl_loader_headers.len() {
-                                        if sel4_state.capdl_loader_headers[i] == None {
-                                            next_slot = Some(i);
-                                            break;
-                                        }
-                                    }
-                                    match next_slot {
-                                        Some(slot) => {
-                                            sel4_state.capdl_loader_headers[slot] = Some(phdr);
-                                        }
-                                        None => panic!("380"),
-                                    }
-                                    self.sel4_state.replace(sel4_state);
-                                }
-                                _ => panic!("317"),
-                            };
-                        });
+//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() {
+                            if sel4_state.phdrs[i] == None {
+                                next_slot = Some(i);
+                                break;
+                            }
+                        }
+                        if let Some(slot) = next_slot {
+                            sel4_state.phdrs[slot] = Some(phdr);
+                        } else {
+                            panic!("Too many ELF program headers");
+                        }
+                        self.sel4_state.replace(sel4_state);
                     }
                 }
-                _ => panic!("335"),
+                _ => unreachable!(),
             };
         });
         match self.state.get() {
@@ -462,18 +398,64 @@
                     ));
                     self.read_page(cursor);
                 } else {
-                    self.state.set(ElfLoaderState::Idle);
-                    self.run_next_task();
+                    // Record state derived from phdrs before they get clobbered.
+                    let mut segment_load_offset = 0;
+                    self.current_task.map(|task| {
+                        let mut sel4_state = self.sel4_state.get();
+                        match task {
+                            LoadTasks::LoadElf("kernel") => {
+                                // Stash end of kernel for loading capdl-loader
+                                sel4_state.kernel_phys_max = matcha_utils::elf_loader::elf_phys_max_opt(&sel4_state.phdrs);
+
+                                dprintf!("Loading seL4 kernel\r\n");
+                                segment_load_offset = 0;
+                            }
+                            LoadTasks::LoadElf("capdl-loader") => {
+                                sel4_state.capdl_phys_max = matcha_utils::elf_loader::elf_phys_max_opt(&sel4_state.phdrs);
+                                sel4_state.capdl_phys_min = matcha_utils::elf_loader::elf_phys_min_opt(&sel4_state.phdrs);
+
+                                dprintf!("Loading capdl-loader to the page after seL4\r\n");
+                                segment_load_offset = matcha_utils::round_up_to_page(
+                                    sel4_state.kernel_phys_max
+                                ) - sel4_state.capdl_phys_min;
+                            }
+                            _ => unreachable!(),
+                        };
+                        self.sel4_state.replace(sel4_state);
+                    });
+                    self.load_elf_segments(segment_load_offset);
                 }
             }
-            _ => panic!("375"),
+            _ => unreachable!(),
         }
     }
 
+    // Initiates loading the ELF segments for the current file.
+    fn load_elf_segments(&self, offset: u32) {
+        let phdr = self.sel4_state.get().phdrs[0].unwrap();
+        // XXX assumes p_filesz > 0; consolidate with code below
+        let mod_offset = phdr.p_offset % self.page_len;
+        let div_offset = phdr.p_offset / self.page_len;
+        let load_offset = self.sel4_state.get().load_offset;
+        assert!(load_offset  != INVALID_LOAD_OFFSET);
+        let cursor = load_offset + (div_offset * self.page_len);
+        let new_state =
+            ElfLoaderState::LoadSegmentsNew(phdr, 0, cursor, offset, mod_offset, 0);
+        self.state.set(new_state);
+        // XXX wrong, but preserve for now
+        assert!(phdr.p_type == PT_LOAD && phdr.p_filesz > 0);
+        print_segment(0, cursor, offset, &phdr);
+        self.read_page(cursor);
+    }
+
+    // Callback from |read_complete| after reading a page for the current
+    // loadable program segment. Data may be copied to the SMC memory or
+    // SMC memory may be zero'd (e.g. for bss). If the segment is completed
+    // advamce to the next load segment. If all segments have been processed
+    // advance to the next task.
     fn load_segment_callback(&self) {
         match self.state.get() {
             ElfLoaderState::LoadSegmentsNew(
-                name,
                 phdr,
                 index,
                 cursor,
@@ -481,133 +463,69 @@
                 mod_offset,
                 loaded,
             ) => {
+                // Copy read data to SMC ram.
                 let bytes_in_this_page = self.page_len - mod_offset;
-                // Copy data into SMC ram.
                 self.read_page.map(|page| {
                     let mut_page = page.as_mut();
+                    // NB: read_page always reads a full page from flash, maybe copy less
+                    let bytes_to_copy =
+                        if loaded + bytes_in_this_page >= phdr.p_filesz {
+                            phdr.p_filesz - loaded
+                        } else { bytes_in_this_page };
                     smc_ram_memcpy(
                         &mut mut_page[(mod_offset as usize)..],
                         phdr.p_paddr + offset + loaded,
-                        bytes_in_this_page as usize,
+                        bytes_to_copy as usize,
                     );
                 });
                 let progress = loaded + bytes_in_this_page;
                 if progress < phdr.p_filesz {
-                    // Another page
-                    let new_cursor = cursor + self.page_len;
+                    // Same program header, more file data; issue a new read.
+                    let next_cursor = cursor + self.page_len;
                     let new_state = ElfLoaderState::LoadSegmentsNew(
-                        name, phdr, index, new_cursor, offset, 0, progress,
+                        phdr, index, next_cursor, offset, /*mod_offset=*/ 0, progress,
                     );
                     self.state.set(new_state);
-                    self.read_page(new_cursor);
+                    self.read_page(next_cursor);
                 } else {
-                    let segments = match name {
-                        "kernel" => self.sel4_state.get().kernel_headers,
-                        "capdl-loader" => self.sel4_state.get().capdl_loader_headers,
-                        _ => panic!("461"),
-                    };
+                    // No more file data to read for this program header.
+                    if phdr.p_filesz < phdr.p_memsz {
+                        // Zero-pad remainder of segment.
+                        let zero_bytes = phdr.p_memsz - phdr.p_filesz;
+                        matcha_utils::smc_ram_zero(
+                            phdr.p_paddr + offset + phdr.p_filesz,
+                            zero_bytes as usize
+                        );
+                    }
+                    // Current segment is complete, advance to the next segment.
                     let next_index = index + 1;
-                    let mut last_segment = false;
-                    if next_index == segments.len() {
-                        last_segment = true;
-                    }
-                    if !last_segment {
-                        last_segment = match segments[next_index] {
-                            Some(_) => false,
-                            None => true,
-                        };
-                    }
-                    if last_segment {
+                    if next_index >= self.sel4_state.get().phdrs.len() ||
+                       self.sel4_state.get().phdrs[next_index] == None {
+                        // Last load segment, advance to the next task.
                         self.state.set(ElfLoaderState::Idle);
                         self.run_next_task();
                     } else {
-                        let next_segment = segments[next_index].unwrap();
-                        if next_segment.p_memsz != 0 && next_segment.p_filesz == 0 {
-                            // some bss segment, let's clear the memory.
-                            matcha_utils::smc_ram_zero(next_segment.p_paddr + offset, next_segment.p_memsz as usize);
-                        }
-                        self.state.set(ElfLoaderState::Idle);
-                        self.run_next_task();
-                        // TODO(atv): Finish this here.
-                        // panic!("476");
-                        // let next_segment = segments[next_index];
-                        // let new_cursor = ?;
-                        // let mod_offset = ?;
-                        // let new_state = ElfLoaderState::LoadSegmentsNew(name, next_segment, next_index, new_cursor, offset, mod_offset, 0);
-                        // self.state.set(new_state);
-                        // self.read_page(new_cursor);
+                        // Setup reading the next load segment.
+                        let next_phdr = self.sel4_state.get().phdrs[next_index].unwrap();
+                        let (mod_offset, div_offset) = if next_phdr.p_filesz > 0 {
+                            (next_phdr.p_offset % self.page_len, next_phdr.p_offset / self.page_len)
+                        } else {
+                            (self.page_len, 0)
+                        };
+                        let next_cursor = self.sel4_state.get().load_offset + (div_offset * self.page_len);
+
+                        print_segment(next_index, next_cursor, offset, &next_phdr);
+
+                        let new_state =
+                            ElfLoaderState::LoadSegmentsNew(next_phdr, next_index, next_cursor, offset, mod_offset, 0);
+                        self.state.set(new_state);
+                        self.read_page(next_cursor);
                     }
                 }
             }
-            _ => panic!("445"),
+            _ => unreachable!(),
         }
     }
-
-    fn find_file_callback(&self) {
-        self.read_page.map(|page| {
-            let mut_page = page.as_mut();
-            let tar_header = TarHeader::from_bytes(mut_page);
-            match self.state.get() {
-                ElfLoaderState::FindingFile(name, cursor) => {
-                    let found_file = tar_header.name().contains(name);
-                    self.current_task.map(|task| match task {
-                        LoadTasks::FindFile(_file) => {
-                            if found_file {
-                                let mut sel4_state = self.sel4_state.get();
-                                match name {
-                                    "kernel" => {
-                                        sel4_state.kernel_offset = cursor + self.page_len;
-                                    }
-                                    "capdl-loader" => {
-                                        sel4_state.capdl_loader_offset = cursor + self.page_len;
-                                    }
-                                    _ => {}
-                                }
-                                self.sel4_state.replace(sel4_state);
-                                self.state.set(ElfLoaderState::Idle);
-                            } else {
-                                let new_cursor =
-                                    self.page_len + cursor + ((tar_header.size() + 511) & !511);
-                                self.state
-                                    .set(ElfLoaderState::FindingFile(name, new_cursor));
-                            }
-                        }
-                        LoadTasks::LoadElf(_file) => {
-                            if found_file {
-                                self.state
-                                    .set(ElfLoaderState::LoadElfHeader(cursor + self.page_len));
-                            } else {
-                                let new_cursor =
-                                    self.page_len + cursor + ((tar_header.size() + 511) & !511);
-                                self.state
-                                    .set(ElfLoaderState::FindingFile(name, new_cursor));
-                            }
-                        }
-                        _ => panic!("487"),
-                    })
-                }
-                _ => panic!("491"),
-            };
-        });
-
-        match self.state.get() {
-            ElfLoaderState::FindingFile(_, cursor) => self.read_page(cursor),
-            ElfLoaderState::LoadElfHeader(cursor) => self.read_page(cursor),
-            ElfLoaderState::Idle => self.run_next_task(),
-            _ => panic!("507"),
-        }
-    }
-
-    fn read_page(&self, page: u32) {
-        self.flash.map(|flash| {
-            self.read_page.take().map(|read_page| {
-                self.flash_busy.set(true);
-                if let Err((_, buf)) = flash.read_page((page / self.page_len) as usize, read_page) {
-                    self.read_page.replace(buf);
-                }
-            });
-        });
-    }
 }
 
 impl<'a, F: hil::flash::Flash> Driver for ElfLoaderCapsule<'a, F> {