| #include "bios.h" |
| #include "stdio.h" |
| #include "ioport.h" |
| #include "string.h" |
| #include "linuxboot.h" |
| |
| #define CBFS_HEADER_MAGIC 0x4F524243 // ORBC |
| #define CBFS_HEADER_VERSION1 0x31313131 |
| #define CBFS_HEADER_VERSION2 0x31313132 |
| |
| static const char file_magic[8] = "LARCHIVE"; |
| |
| struct cbfs_header { |
| uint32_t magic; |
| uint32_t version; |
| uint32_t romsize; |
| uint32_t bootblocksize; |
| uint32_t align; |
| uint32_t offset; |
| uint32_t pad[2]; |
| } __attribute__((__packed__)); |
| |
| struct cbfs_file_header { |
| char magic[8]; |
| uint32_t len; |
| uint32_t type; |
| uint32_t checksum; |
| uint32_t offset; |
| } __attribute__((__packed__)); |
| |
| |
| struct cbfs_file { |
| uint32_t size; |
| void *buf; |
| char name[57]; |
| struct cbfs_file *next; |
| }; |
| |
| static struct cbfs_file *files; |
| |
| static inline uint32_t ldl_le_p(void *p) |
| { |
| uint32_t val; |
| memcpy(&val, p, 4); |
| return val; |
| } |
| |
| static inline uint32_t ldl_be_p(void *p) |
| { |
| uint32_t val; |
| memcpy(&val, p, 4); |
| return __builtin_bswap32(val); |
| } |
| |
| bool cbfs_setup(void *base, size_t sz) |
| { |
| uint32_t ofs; |
| struct cbfs_header hdr; |
| struct cbfs_file **pnext; |
| uint32_t align; |
| |
| ofs = sz + (intptr_t)(int32_t)ldl_le_p(base + sz - 4); |
| if (ofs >= sz - sizeof(struct cbfs_header) || |
| ldl_be_p(base + ofs) != CBFS_HEADER_MAGIC) |
| ofs = 0; |
| |
| for (; ofs + sizeof(struct cbfs_header) < sz; ofs++) { |
| if (ldl_be_p(base + ofs) != CBFS_HEADER_MAGIC) |
| continue; |
| memcpy(&hdr, base + ofs, sizeof(hdr)); |
| if (ldl_be_p(&hdr.version) != CBFS_HEADER_VERSION1 && |
| ldl_be_p(&hdr.version) != CBFS_HEADER_VERSION2) |
| continue; |
| break; |
| } |
| |
| if (ofs + sizeof(struct cbfs_header) >= sz) |
| return false; |
| |
| pnext = &files; |
| ofs = ldl_be_p(&hdr.offset); |
| align = ldl_be_p(&hdr.align); |
| while (ofs + sizeof(struct cbfs_file_header) < sz) { |
| struct cbfs_file_header fhdr; |
| struct cbfs_file *f; |
| |
| memcpy(&fhdr, base + ofs, sizeof(fhdr)); |
| if (memcmp(&fhdr.magic, file_magic, sizeof(file_magic))) |
| break; |
| |
| f = malloc_fseg(sizeof(*f)); |
| *pnext = f; |
| |
| f->size = ldl_be_p(&fhdr.len); |
| f->buf = base + ofs + ldl_be_p(&fhdr.offset); |
| strcpy(f->name, base + ofs + sizeof(fhdr)); |
| f->next = NULL; |
| pnext = &f->next; |
| |
| ofs = f->buf + f->size - base; |
| ofs = (ofs + align - 1) & -align; |
| } |
| return files != NULL; |
| } |
| |
| static struct cbfs_file *cbfs_file_find(const char *name) |
| { |
| struct cbfs_file *f = files; |
| |
| for (f = files; f; f = f->next) |
| if (!strcmp(name, f->name)) |
| return f; |
| |
| return NULL; |
| } |
| |
| uint32_t cbfs_size(const char *name) |
| { |
| struct cbfs_file *f = cbfs_file_find(name); |
| if (!f) |
| return 0; |
| |
| return f->size; |
| } |
| |
| void cbfs_read(const char *name, void *buf, size_t size, size_t skip) |
| { |
| struct cbfs_file *f = cbfs_file_find(name); |
| if (!f) |
| panic(); |
| |
| if (skip > f->size) |
| return; |
| if (size + skip > f->size) |
| size = f->size - skip; |
| memcpy(buf, f->buf + skip, size); |
| } |
| |
| bool cbfs_boot(void) |
| { |
| struct linuxboot_args args; |
| |
| args.vmlinuz_size = cbfs_size("vmlinuz"); |
| if (!args.vmlinuz_size) |
| return false; |
| |
| args.initrd_size = cbfs_size("initrd"); |
| args.cmdline_size = cbfs_size("cmdline"); |
| |
| cbfs_read("vmlinuz", args.header, sizeof(args.header), 0); |
| if (!parse_bzimage(&args)) |
| return false; |
| |
| cbfs_read("vmlinuz", args.setup_addr, args.setup_size, 0); |
| cbfs_read("vmlinuz", args.kernel_addr, args.kernel_size, args.setup_size); |
| |
| if (args.initrd_size) |
| cbfs_read("initrd", args.initrd_addr, args.initrd_size, 0); |
| if (args.cmdline_size) |
| cbfs_read("cmdline", args.cmdline_addr, args.cmdline_size, 0); |
| |
| boot_bzimage(&args); |
| return true; |
| } |
| |
| bool boot_from_cbfs(void *base, size_t sz) |
| { |
| return cbfs_setup(base, sz) && cbfs_boot(); |
| } |
| |