Initial support for running CBFS payloads.

Add boot menu option for CBFS payloads.
Rework "override" system so that it is done per BEV.
Add file prefix scanning code to CBFS.
Add CBFS payload launching support.
diff --git a/src/boot.c b/src/boot.c
index 6b4e985..fe1c6bf 100644
--- a/src/boot.c
+++ b/src/boot.c
@@ -47,6 +47,12 @@
         ie++;
     }
 
+    if (CONFIG_COREBOOT_FLASH) {
+        ie->type = IPL_TYPE_CBFS;
+        ie->description = "CBFS";
+        ie++;
+    }
+
     IPL.bevcount = ie - IPL.bev;
     SET_EBDA(boot_sequence, 0xffff);
     if (CONFIG_COREBOOT) {
@@ -170,6 +176,23 @@
     return ATA.cdcount;
 }
 
+// Show coreboot-fs menu item.
+static int
+menu_show_cbfs(struct ipl_entry_s *ie, int menupos)
+{
+    int count = 0;
+    for (;;) {
+        const char *filename = cbfs_findNprefix("img/", count);
+        if (!filename)
+            break;
+        printf("%d. Payload [%s]\n", menupos + count, &filename[4]);
+        count++;
+        if (count > 8)
+            break;
+    }
+    return count;
+}
+
 // Show IPL option menu.
 static void
 interactive_bootmenu()
@@ -208,6 +231,9 @@
         case IPL_TYPE_CDROM:
             sc = menu_show_cdrom(ie, menupos);
             break;
+        case IPL_TYPE_CBFS:
+            sc = menu_show_cbfs(ie, menupos);
+            break;
         default:
             sc = menu_show_default(ie, menupos);
             break;
@@ -231,17 +257,7 @@
             choice -= subcount[bev];
             bev++;
         }
-
-        switch (IPL.bev[bev].type) {
-        case IPL_TYPE_HARDDISK:
-            // A harddrive request enables a BCV order.
-            IPL.bcv_override = choice-1;
-            break;
-        case IPL_TYPE_CDROM:
-            // Select cdrom to boot from.
-            IPL.cdrom_override = choice-1;
-            break;
-        }
+        IPL.bev[bev].subchoice = choice-1;
 
         // Add user choice to the boot order.
         IPL.bootorder = (IPL.bootorder << 4) | (bev+1);
@@ -275,7 +291,7 @@
     interactive_bootmenu();
 
     // Run BCVs
-    int override = IPL.bcv_override;
+    int override = IPL.bev[1].subchoice;
     if (override < IPL.bcvcount)
         run_bcv(&IPL.bcv[override]);
     int i;
@@ -343,11 +359,11 @@
 
 // Boot from a CD-ROM
 static void
-boot_cdrom()
+boot_cdrom(struct ipl_entry_s *ie)
 {
     if (! CONFIG_CDROM_BOOT)
         return;
-    int status = cdrom_boot(IPL.cdrom_override);
+    int status = cdrom_boot(ie->subchoice);
     if (status) {
         printf("Boot failed: Could not read from CDROM (code %04x)\n", status);
         return;
@@ -363,6 +379,18 @@
     call_boot_entry(bootseg, bootip, bootdrv);
 }
 
+// Boot from a CD-ROM
+static void
+boot_cbfs(struct ipl_entry_s *ie)
+{
+    if (! CONFIG_COREBOOT_FLASH)
+        return;
+    const char *filename = cbfs_findNprefix("img/", ie->subchoice);
+    if (! filename)
+        return;
+    cbfs_run_payload(filename);
+}
+
 static void
 do_boot(u16 seq_nr)
 {
@@ -403,7 +431,10 @@
         boot_disk(0x80, 1);
         break;
     case IPL_TYPE_CDROM:
-        boot_cdrom();
+        boot_cdrom(ie);
+        break;
+    case IPL_TYPE_CBFS:
+        boot_cbfs(ie);
         break;
     case IPL_TYPE_BEV:
         call_boot_entry(ie->vector >> 16, ie->vector & 0xffff, 0);
diff --git a/src/boot.h b/src/boot.h
index f251096..816fed5 100644
--- a/src/boot.h
+++ b/src/boot.h
@@ -9,7 +9,7 @@
 
 struct ipl_entry_s {
     u16 type;
-    u16 flags;
+    u16 subchoice;
     u32 vector;
     const char *description;
 };
@@ -18,7 +18,6 @@
     struct ipl_entry_s bev[8];
     struct ipl_entry_s bcv[8];
     int bevcount, bcvcount;
-    int bcv_override, cdrom_override;
     u32 bootorder;
     int checkfloppysig;
 };
@@ -26,6 +25,7 @@
 #define IPL_TYPE_FLOPPY      0x01
 #define IPL_TYPE_HARDDISK    0x02
 #define IPL_TYPE_CDROM       0x03
+#define IPL_TYPE_CBFS        0x20
 #define IPL_TYPE_BEV         0x80
 
 
diff --git a/src/coreboot.c b/src/coreboot.c
index 9fd5e0f..feccad3 100644
--- a/src/coreboot.c
+++ b/src/coreboot.c
@@ -322,34 +322,72 @@
     u32 type;
     u32 checksum;
     u32 offset;
+    char filename[0];
 } PACKED;
 
 static struct cbfs_file *
-cbfs_find(char *fname)
+cbfs_search(struct cbfs_file *file)
 {
-    if (! CONFIG_COREBOOT_FLASH)
-        return NULL;
-    if (! CBHDR)
-        return NULL;
-
-    dprintf(3, "Searching CBFS for %s\n", fname);
-
-    struct cbfs_file *file = (void *)(0 - ntohl(CBHDR->romsize) + ntohl(CBHDR->offset));
     for (;;) {
         if (file < (struct cbfs_file *)(0xFFFFFFFF - ntohl(CBHDR->romsize)))
             return NULL;
-        if (file->magic != CBFS_FILE_MAGIC) {
-            file = (void*)file + ntohl(CBHDR->align);
-            continue;
-        }
-
-        dprintf(3, "Found CBFS file %s\n", (char*)file + sizeof(*file));
-        if (streq(fname, (char*)file + sizeof(*file)))
+        if (file->magic == CBFS_FILE_MAGIC)
             return file;
-        file = (void*)file + ALIGN(ntohl(file->len) + ntohl(file->offset), ntohl(CBHDR->align));
+        file = (void*)file + ntohl(CBHDR->align);
     }
 }
 
+static struct cbfs_file *
+cbfs_getfirst()
+{
+    if (! CBHDR)
+        return NULL;
+    return cbfs_search((void *)(0 - ntohl(CBHDR->romsize) + ntohl(CBHDR->offset)));
+}
+
+static struct cbfs_file *
+cbfs_getnext(struct cbfs_file *file)
+{
+    file = (void*)file + ALIGN(ntohl(file->len) + ntohl(file->offset), ntohl(CBHDR->align));
+    return cbfs_search(file);
+}
+
+static struct cbfs_file *
+cbfs_findfile(const char *fname)
+{
+    if (! CONFIG_COREBOOT_FLASH)
+        return NULL;
+
+    dprintf(3, "Searching CBFS for %s\n", fname);
+    struct cbfs_file *file;
+    for (file = cbfs_getfirst(); file; file = cbfs_getnext(file)) {
+        dprintf(3, "Found CBFS file %s\n", file->filename);
+        if (streq(fname, file->filename))
+            return file;
+    }
+    return NULL;
+}
+
+const char *
+cbfs_findNprefix(const char *prefix, int n)
+{
+    if (! CONFIG_COREBOOT_FLASH)
+        return NULL;
+
+    dprintf(3, "Searching CBFS for prefix %s\n", prefix);
+    int len = strlen(prefix);
+    struct cbfs_file *file;
+    for (file = cbfs_getfirst(); file; file = cbfs_getnext(file)) {
+        dprintf(3, "Found CBFS file %s\n", file->filename);
+        if (memeq(prefix, file->filename, len)) {
+            if (n <= 0)
+                return file->filename;
+            n--;
+        }
+    }
+    return NULL;
+}
+
 static char
 getHex(u8 x)
 {
@@ -375,14 +413,14 @@
 
     char fname[17];
     // Ughh - poor man's sprintf of "pci%04x,%04x.rom"
-    *(u32*)fname = 0x20696370; // "pci"
+    *(u32*)fname = 0x20696370; // "pci "
     *(u32*)&fname[3] = hexify4(vendev);
     fname[7] = ',';
     *(u32*)&fname[8] = hexify4(vendev >> 16);
     *(u32*)&fname[12] = 0x6d6f722e; // ".rom"
     fname[16] = '\0';
 
-    struct cbfs_file *file = cbfs_find(fname);
+    struct cbfs_file *file = cbfs_findfile(fname);
     if (!file)
         return NULL;
     // Found it.
@@ -390,6 +428,68 @@
     return (void*)file + ntohl(file->offset);
 }
 
+struct cbfs_payload_segment {
+    u32 type;
+    u32 compression;
+    u32 offset;
+    u64 load_addr;
+    u32 len;
+    u32 mem_len;
+} PACKED;
+
+#define PAYLOAD_SEGMENT_BSS    0x20535342
+#define PAYLOAD_SEGMENT_ENTRY  0x52544E45
+
+#define CBFS_COMPRESS_NONE  0
+
+struct cbfs_payload {
+    struct cbfs_payload_segment segments[1];
+};
+
+void
+cbfs_run_payload(const char *filename)
+{
+    dprintf(1, "Run %s\n", filename);
+    struct cbfs_file *file = cbfs_findfile(filename);
+    if (!file)
+        return;
+    struct cbfs_payload *pay = (void*)file + ntohl(file->offset);
+    struct cbfs_payload_segment *seg = pay->segments;
+    for (;;) {
+        if (seg->compression != htonl(CBFS_COMPRESS_NONE)) {
+            dprintf(1, "No support for compressed payloads (%x)\n"
+                    , seg->compression);
+            return;
+        }
+        void *src = (void*)pay + ntohl(seg->offset);
+        void *dest = (void*)ntohl((u32)seg->load_addr);
+        u32 src_len = ntohl(seg->len);
+        u32 dest_len = ntohl(seg->mem_len);
+        switch (seg->type) {
+        case PAYLOAD_SEGMENT_BSS:
+            dprintf(3, "BSS segment %d@%p\n", dest_len, dest);
+            memset(dest, 0, dest_len);
+            break;
+        case PAYLOAD_SEGMENT_ENTRY: {
+            dprintf(1, "Calling addr %p\n", dest);
+            void (*func)() = dest;
+            func();
+            return;
+        }
+        default:
+            dprintf(3, "Segment %x %d@%p -> %d@%p\n"
+                    , seg->type, src_len, src, dest_len, dest);
+            if (src_len > dest_len)
+                src_len = dest_len;
+            memcpy(dest, src, src_len);
+            if (dest_len > src_len)
+                memset(dest + src_len, 0, dest_len - src_len);
+            break;
+        }
+        seg++;
+    }
+}
+
 void
 coreboot_setup(void)
 {
diff --git a/src/util.c b/src/util.c
index 2b46ea2..df0e993 100644
--- a/src/util.c
+++ b/src/util.c
@@ -117,9 +117,34 @@
     return checksum_far(GET_SEG(SS), buf, len);
 }
 
+size_t
+strlen(const char *s)
+{
+    if (__builtin_constant_p(s))
+        return __builtin_strlen(s);
+    const char *p = s;
+    while (*p)
+        p++;
+    return p-s;
+}
+
+// Compare two areas of memory.
+int
+memeq(const void *s1, const void *s2, size_t n)
+{
+    while (n) {
+        if (*(u8*)s1 != *(u8*)s2)
+            return 0;
+        s1++;
+        s2++;
+        n--;
+    }
+    return 1;
+}
+
 // Compare two strings.
 int
-streq(char *s1, char *s2)
+streq(const char *s1, const char *s2)
 {
     for (;;) {
         if (*s1 != *s2)
diff --git a/src/util.h b/src/util.h
index 9ce220a..cb2d46f 100644
--- a/src/util.h
+++ b/src/util.h
@@ -69,7 +69,9 @@
 inline u32 stack_hop(u32 eax, u32 edx, u32 ecx, void *func);
 u8 checksum_far(u16 buf_seg, void *buf_far, u32 len);
 u8 checksum(void *buf, u32 len);
-int streq(char *s1, char *s2);
+int memeq(const void *s1, const void *s2, size_t n);
+size_t strlen(const char *s);
+int streq(const char *s1, const char *s2);
 void *memset(void *s, int c, size_t n);
 void *memcpy(void *d1, const void *s1, size_t len);
 inline void memcpy_far(u16 d_seg, void *d_far
@@ -165,7 +167,9 @@
 void smbios_init(void);
 
 // coreboot.c
+const char *cbfs_findNprefix(const char *prefix, int n);
 void *cb_find_optionrom(u32 vendev);
+void cbfs_run_payload(const char *filename);
 void coreboot_setup();
 
 // vgahooks.c