bztsrc--bootboot/mkbootimg/main.c

568 lines
30 KiB
C

/*
* mkbootimg/main.c
*
* Copyright (C) 2017 - 2021 bzt (bztsrc@gitlab)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This file is part of the BOOTBOOT Protocol package.
* @brief Bootable image creator main file
*
*/
#include "main.h"
#include "fs.h"
#ifdef __WIN32__
# include <windows.h>
# define ISHH(x) ((((x)>>30)&0xFFFFFFFF)==0xFFFFFFFF)
# define LL "I64"
#else
# define ISHH(x) (((x)>>30)==0x3FFFFFFFF)
# if defined(MACOSX) || __WORDSIZE == 32
# define LL "ll"
# else
# define LL "l"
# endif
#endif
extern const char deflate_copyright[];
char **lang = NULL;
/**
* Get language dictionary
*/
char **getlang(int *argc, char **argv)
{
char *loc = NULL;
int i;
#ifdef __WIN32__
/* see https://docs.microsoft.com/en-us/windows/win32/intl/language-identifier-constants-and-strings */
switch((GetUserDefaultLangID() /* GetUserDefaultUILanguage() */) & 0xFF) {
case 0x01: loc = "ar"; break; case 0x02: loc = "bg"; break;
case 0x03: loc = "ca"; break; case 0x04: loc = "zh"; break;
case 0x05: loc = "cs"; break; case 0x06: loc = "da"; break;
case 0x07: loc = "de"; break; case 0x08: loc = "el"; break;
case 0x0A: loc = "es"; break; case 0x0B: loc = "fi"; break;
case 0x0C: loc = "fr"; break; case 0x0D: loc = "he"; break;
case 0x0E: loc = "hu"; break; case 0x0F: loc = "is"; break;
case 0x10: loc = "it"; break; case 0x11: loc = "jp"; break;
case 0x12: loc = "ko"; break; case 0x13: loc = "nl"; break;
case 0x14: loc = "no"; break; case 0x15: loc = "pl"; break;
case 0x16: loc = "pt"; break; case 0x17: loc = "rm"; break;
case 0x18: loc = "ro"; break; case 0x19: loc = "ru"; break;
case 0x1A: loc = "hr"; break; case 0x1B: loc = "sk"; break;
case 0x1C: loc = "sq"; break; case 0x1D: loc = "sv"; break;
case 0x1E: loc = "th"; break; case 0x1F: loc = "tr"; break;
case 0x20: loc = "ur"; break; case 0x21: loc = "id"; break;
case 0x22: loc = "uk"; break; case 0x23: loc = "be"; break;
case 0x24: loc = "sl"; break; case 0x25: loc = "et"; break;
case 0x26: loc = "lv"; break; case 0x27: loc = "lt"; break;
case 0x29: loc = "fa"; break; case 0x2A: loc = "vi"; break;
case 0x2B: loc = "hy"; break; case 0x2D: loc = "bq"; break;
case 0x2F: loc = "mk"; break; case 0x36: loc = "af"; break;
case 0x37: loc = "ka"; break; case 0x38: loc = "fo"; break;
case 0x39: loc = "hi"; break; case 0x3A: loc = "mt"; break;
case 0x3C: loc = "gd"; break; case 0x3E: loc = "ms"; break;
case 0x3F: loc = "kk"; break; case 0x40: loc = "ky"; break;
case 0x45: loc = "bn"; break; case 0x47: loc = "gu"; break;
case 0x4D: loc = "as"; break; case 0x4E: loc = "mr"; break;
case 0x4F: loc = "sa"; break; case 0x53: loc = "kh"; break;
case 0x54: loc = "lo"; break; case 0x56: loc = "gl"; break;
case 0x5E: loc = "am"; break; case 0x62: loc = "fy"; break;
case 0x68: loc = "ha"; break; case 0x6D: loc = "ba"; break;
case 0x6E: loc = "lb"; break; case 0x6F: loc = "kl"; break;
case 0x7E: loc = "br"; break; case 0x92: loc = "ku"; break;
case 0x09: default: loc = "en"; break;
}
#endif
if(!loc) loc = getenv("LANG");
if(!loc) loc = "en";
if(*argc > 2 && !strcmp(argv[1], "-l")) { loc = argv[2]; (*argc) -= 2; argv += 2; }
for(i = 0; i < NUMLANGS; i++)
if(!strncmp(loc, dict[i][0], strlen(dict[i][0]))) break;
if(i >= NUMLANGS) { i = 0; loc = "en"; }
lang = &dict[i][1];
return argv;
}
/**
* Parse the mkbootimg json configuration file
*/
void parsejson(char *json)
{
char *tmp, key[64];
int i;
tmp = json_get(json, "diskguid"); getguid(tmp, &diskguid); free(tmp);
tmp = json_get(json, "disksize"); if(tmp) { disk_size = atoi(tmp); } free(tmp);
tmp = json_get(json, "align"); if(tmp) { disk_align = atoi(tmp); } free(tmp);
memset(initrd_dir, 0, NUMARCH*sizeof(void*));
memset(initrd_buf, 0, NUMARCH*sizeof(void*));
for(i = 0; i < NUMARCH; i++) {
sprintf(key, "initrd.file.%d", i);
tmp = json_get(json, key);
if(!i && (!tmp || !*tmp)) tmp = json_get(json, "initrd.file");
if(tmp && *tmp) {
initrd_buf[i] = readfileall(tmp);
initrd_size[i] = read_size;
if(!initrd_buf[i] || !read_size) { fprintf(stderr,"mkbootimg: %s %s\r\n",lang[ERR_INITRDIMG],tmp); exit(1); }
free(tmp);
} else {
sprintf(key, "initrd.directory.%d", i);
initrd_dir[i] = json_get(json, key);
if(!i && !initrd_dir[i]) initrd_dir[i] = json_get(json, "initrd.directory");
}
if(!initrd_dir[i] && !initrd_buf[i]) break;
}
if((!initrd_dir[0] || !initrd_dir[0][0]) && !initrd_buf[0]) { fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_NOINITRD]); exit(1); }
if(initrd_dir[0]) {
tmp = json_get(json, "initrd.type");
if(!tmp || !*tmp) { fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_NOINITRDTYPE]); exit(1); }
if(strcmp(tmp, "fat16") && strcmp(tmp, "fat32"))
for(i = 0; fsdrv[i].name && fsdrv[i].add; i++)
if(!strcmp(tmp, fsdrv[i].name)) { rd_open = fsdrv[i].open; rd_add = fsdrv[i].add; rd_close = fsdrv[i].close; break; }
if(!rd_add) {
fprintf(stderr,"mkbootimg: %s %s. %s:", lang[ERR_BADINITRDTYPE],tmp,lang[ERR_ACCEPTVALUES]);
for(i = 0; fsdrv[i].name && fsdrv[i].add; i++) fprintf(stderr,"%s %s",i ? "," : "",fsdrv[i].name);
fprintf(stderr,"\r\n");
exit(1);
}
free(tmp);
}
tmp = json_get(json, "initrd.gzip");
if(tmp && tmp[0] != '1' && tmp[0] != 't' && tmp[0] != 'y') initrd_gzip = 0;
free(tmp);
tmp = json_get(json, "config");
if(tmp && *tmp) {
config = (char*)readfileall(tmp);
if(!config || !*config) { fprintf(stderr,"mkbootimg: %s %s\r\n",lang[ERR_NOCONF],tmp); exit(1); }
if(read_size > 4095) { fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_BIGCONF]); exit(1); }
}
free(tmp);
tmp = json_get(json, "iso9660"); if(tmp && (*tmp=='1' || *tmp=='t' || *tmp=='y')) { iso9660 = 1; } free(tmp);
tmp = json_get(json, "partitions.0.type");
if(!tmp || !*tmp) { fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_NOPART]); exit(1); }
if(tmp && !memcmp(tmp, "fat32", 5)) boot_fat = 32;
free(tmp);
tmp = json_get(json, "partitions.0.size");
if(!tmp || !*tmp) { fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_NOPARTSIZE]); exit(1); }
boot_size = atoi(tmp); free(tmp); if(boot_size < 8) boot_size = 8;
if(!diskguid.Data1) diskguid.Data1 = crc32(0,(uint8_t*)&t, sizeof(time_t)) ^ 0x08040201;
if(!diskguid.Data2 && !diskguid.Data3) {
((uint32_t*)&diskguid)[1] = crc32(0,(uint8_t*)&diskguid.Data1, 4);
((uint32_t*)&diskguid)[2] = crc32(0,(uint8_t*)&diskguid.Data2, 4) ^ (unsigned long int)t;
((uint32_t*)&diskguid)[3] = crc32(0,(uint8_t*)&diskguid.Data3, 4);
}
}
/**
* Parse the BOOTBOOT configuration file
*/
void parseconfig()
{
char *ptr = config, *e;
while(ptr && *ptr) {
if(ptr[0]==' '||ptr[0]=='\t'||ptr[0]=='\r'||ptr[0]=='\n') { ptr++; continue; }
if((ptr[0]=='/'&&ptr[1]=='/')||ptr[0]=='#') { while(ptr[0]!=0 && ptr[0]!='\r' && ptr[0]!='\n') ptr++; }
if(ptr[0]=='/'&&ptr[1]=='*') { ptr+=2; while(ptr[0]!=0 && ptr[-1]!='*' && ptr[0]!='/') ptr++; }
if(!memcmp(ptr, "kernel=", 7)) {
ptr += 7; for(e = ptr; *e && *e != '\r' && *e != '\n'; e++);
kernelname = malloc(e - ptr + 1);
if(!kernelname) { fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_MEM]); exit(1); }
memcpy(kernelname, ptr, e - ptr); kernelname[e - ptr] = 0;
break;
}
ptr++;
}
if(!kernelname || !*kernelname) {
kernelname = malloc(10);
if(!kernelname) { fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_MEM]); exit(1); }
strcpy(kernelname, "sys/core");
}
}
/**
* Parse the ELF or PE kernel executable
*/
void parsekernel(int idx, unsigned char *data, int v)
{
Elf64_Ehdr *ehdr;
Elf64_Phdr *phdr;
Elf64_Shdr *shdr, *strt, *sym_sh = NULL, *str_sh = NULL;
Elf64_Sym *sym = NULL, *s;
pe_hdr *pehdr;
pe_sym *ps;
uint32_t i, n = 0, bss = 0, strsz = 0, syment = 0, ma, fa;
uint64_t core_ptr = 0, core_size = 0, core_addr = 0, entrypoint = 0, mm_addr = 0, fb_addr = 0, bb_addr = 0, env_addr = 0;
uint64_t initstack = 0;
char *strtable, *name;
ehdr=(Elf64_Ehdr *)(data);
pehdr=(pe_hdr*)(data + ((mz_hdr*)(data))->peaddr);
/* do not translate stdout, it might be parsed by scripts. Only translate stderr */
if(v) printf("File format: ");
if((!memcmp(ehdr->e_ident,ELFMAG,SELFMAG)||!memcmp(ehdr->e_ident,"OS/Z",4)) &&
ehdr->e_ident[EI_CLASS]==ELFCLASS64 && ehdr->e_ident[EI_DATA]==ELFDATA2LSB) {
if(v) printf("ELF64\r\nArchitecture: %s\r\n", ehdr->e_machine==EM_AARCH64 ? "AArch64" : (ehdr->e_machine==EM_X86_64 ?
"x86_64" : (ehdr->e_machine==EM_RISCV ? "riscv64" : "invalid")));
if(ehdr->e_machine == EM_AARCH64) { ma = 2*1024*1024-1; fa = 4095; initrd_arch[idx] = 1; } else
if(ehdr->e_machine == EM_X86_64) { ma = 4095; fa = 2*1024*1024-1; initrd_arch[idx] = 2; } else
if(ehdr->e_machine == EM_RISCV) { ma = 4095; fa = 2*1024*1024-1; initrd_arch[idx] = 3; } else
{ fprintf(stderr,"mkbootimg: %s. %s: e_machine 62, 183, 243.\r\n",lang[ERR_BADARCH],lang[ERR_ACCEPTVALUES]); exit(1); }
phdr=(Elf64_Phdr *)((uint8_t *)ehdr+ehdr->e_phoff);
for(i=0;i<ehdr->e_phnum;i++){
if(phdr->p_type==PT_LOAD) {
n++;
core_size = phdr->p_filesz + (ehdr->e_type==3?0x4000:0);
bss = phdr->p_memsz - core_size;
core_ptr = phdr->p_offset;
core_addr = phdr->p_vaddr;
entrypoint = ehdr->e_entry;
/* these are just warnings, hopefully not a problem, but better to be fixed in the kernel linker script */
if(v) {
if(phdr->p_vaddr != phdr->p_paddr)
fprintf(stderr,"mkbootimg: phdr #%d p_vaddr %016" LL "x != p_paddr %016" LL "x ???\r\n",n,phdr->p_vaddr,phdr->p_paddr);
if(phdr->p_align > 4096)
fprintf(stderr,"mkbootimg: phdr #%d %s (p_align %" LL "d)\r\n",n,lang[ERR_PAGEALIGN],phdr->p_align);
}
break;
}
phdr=(Elf64_Phdr *)((uint8_t *)phdr+ehdr->e_phentsize);
}
if(n != 1) { fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_MORESEG]); exit(1); }
if(v) printf("Entry point: %016" LL "x ", entrypoint);
if(entrypoint < core_addr || entrypoint > core_addr+core_size)
{ if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_BADENTRYP]); exit(1); }
if(ehdr->e_shoff > 0) {
shdr = (Elf64_Shdr *)((uint8_t *)ehdr + ehdr->e_shoff);
strt = (Elf64_Shdr *)((uint8_t *)shdr+(uint64_t)ehdr->e_shstrndx*(uint64_t)ehdr->e_shentsize);
strtable = (char *)ehdr + strt->sh_offset;
for(i = 0; i < ehdr->e_shnum; i++){
/* checking shdr->sh_type is not enough, there can be multiple SHT_STRTAB records... */
if(!memcmp(strtable + shdr->sh_name, ".symtab", 8)) sym_sh = shdr;
if(!memcmp(strtable + shdr->sh_name, ".strtab", 8)) str_sh = shdr;
shdr = (Elf64_Shdr *)((uint8_t *)shdr + ehdr->e_shentsize);
}
if(str_sh && sym_sh) {
strtable = (char *)ehdr + str_sh->sh_offset; strsz = str_sh->sh_size;
sym = (Elf64_Sym *)((uint8_t*)ehdr + sym_sh->sh_offset); syment = sym_sh->sh_entsize;
if(str_sh->sh_offset && strsz > 0 && sym_sh->sh_offset && syment > 0)
for(s = sym, i = 0; i<(strtable-(char*)sym)/syment && s->st_name < strsz; i++, s++) {
if(!memcmp(strtable + s->st_name, "bootboot", 9)) bb_addr = s->st_value;
if(!memcmp(strtable + s->st_name, "environment", 12)) env_addr = s->st_value;
if(!memcmp(strtable + s->st_name, "mmio", 4)) mm_addr = s->st_value;
if(!memcmp(strtable + s->st_name, "fb", 3)) fb_addr = s->st_value;
if(!memcmp(strtable + s->st_name, "initstack", 10)) initstack = s->st_value;
}
}
}
} else
if(((mz_hdr*)(data))->magic==MZ_MAGIC && ((mz_hdr*)(data))->peaddr<65536 && pehdr->magic == PE_MAGIC &&
pehdr->file_type == PE_OPT_MAGIC_PE32PLUS) {
if(v) printf("PE32+\r\nArchitecture: %s\r\n", pehdr->machine == IMAGE_FILE_MACHINE_ARM64 ? "AArch64" : (
pehdr->machine == IMAGE_FILE_MACHINE_AMD64 ? "x86_64" : (
pehdr->machine == IMAGE_FILE_MACHINE_RISCV64 ? "riscv64" : "invalid")));
if(pehdr->machine == IMAGE_FILE_MACHINE_ARM64) { ma = 2*1024*1024-1; fa = 4095; initrd_arch[idx] = 1; } else
if(pehdr->machine == IMAGE_FILE_MACHINE_AMD64) { ma = 4095; fa = 2*1024*1024-1; initrd_arch[idx] = 2; } else
if(pehdr->machine == IMAGE_FILE_MACHINE_RISCV64){ma = 4095; fa = 2*1024*1024-1; initrd_arch[idx] = 3; } else
{ fprintf(stderr,"mkbootimg: %s. %s: pe_hdr.machine 0x8664, 0xAA64, 0x5064\r\n",lang[ERR_BADARCH],lang[ERR_ACCEPTVALUES]); exit(1); }
core_size = (pehdr->entry_point-pehdr->code_base) + pehdr->text_size + pehdr->data_size;
bss = pehdr->bss_size;
core_addr = (int64_t)pehdr->code_base;
entrypoint = (int64_t)pehdr->entry_point;
if(v) printf("Entry point: %016" LL "x ", entrypoint);
if(entrypoint < core_addr || entrypoint > core_addr+pehdr->text_size)
{ if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_BADENTRYP]); exit(1); }
if(pehdr->sym_table > 0 && pehdr->numsym > 0) {
strtable = (char *)pehdr + pehdr->sym_table + pehdr->numsym * 18 + 4;
for(i = 0; i < pehdr->numsym; i++) {
ps = (pe_sym*)((uint8_t *)pehdr + pehdr->sym_table + i * 18);
name = !ps->iszero ? (char*)&ps->iszero : strtable + ps->nameoffs;
if(!memcmp(name, "bootboot", 9)) bb_addr = (int64_t)ps->value;
if(!memcmp(name, "environment", 12)) env_addr = (int64_t)ps->value;
if(!memcmp(name, "mmio", 4)) mm_addr = (int64_t)ps->value;
if(!memcmp(name, "fb", 3)) fb_addr = (int64_t)ps->value;
if(!memcmp(name, "initstack", 10)) initstack = (int64_t)ps->value;
i += ps->auxsyms;
}
}
} else {
if(v) printf("unknown\r\n");
fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_INVALIDEXE]);
exit(1);
}
if(v) printf("OK\r\n");
if(mm_addr) {
if(v) printf("mmio: %016" LL "x ", mm_addr);
if(!ISHH(mm_addr)) { if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: mmio %s\r\n",lang[ERR_BADADDR]); exit(1); }
if(mm_addr & ma) { if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: mmio ");fprintf(stderr,lang[ERR_BADALIGN],ma+1);fprintf(stderr,"\r\n"); exit(1); }
if(v) printf("OK\r\n");
}
if(fb_addr) {
if(v) printf("fb: %016" LL "x ", fb_addr);
if(!ISHH(fb_addr)) { if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: fb %s\r\n",lang[ERR_BADALIGN]); exit(1); }
if(fb_addr & fa) { if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: fb ");fprintf(stderr,lang[ERR_BADALIGN],fa+1);fprintf(stderr,"\r\n"); exit(1); }
if((fb_addr >= mm_addr && fb_addr < mm_addr + 16*1024*1024) || (fb_addr + 16*1024*1024 > mm_addr && fb_addr + 16*1024*1024 <= mm_addr + 16*1024*1024))
{ if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: mmio/fb %s\r\n",lang[ERR_ADDRCOL]); exit(1); }
if(v) printf("OK\r\n");
}
if(bb_addr) {
if(v) printf("bootboot: %016" LL "x ", bb_addr);
if(!ISHH(bb_addr)) { if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: bootboot %s\r\n",lang[ERR_BADADDR]); exit(1); }
if(bb_addr & 4095) { if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: bootboot %s\r\n",lang[ERR_PAGEALIGN]); exit(1); }
if((bb_addr >= mm_addr && bb_addr < mm_addr + 16*1024*1024) || (bb_addr + 4096 > mm_addr && bb_addr + 4096 <= mm_addr + 16*1024*1024))
{ if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: mmio/bootboot %s\r\n",lang[ERR_ADDRCOL]); exit(1); }
if((bb_addr >= fb_addr && bb_addr < fb_addr + 16*1024*1024) || (bb_addr + 4096 > fb_addr && bb_addr + 4096 <= fb_addr + 16*1024*1024))
{ if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: fb/bootboot %s\r\n",lang[ERR_ADDRCOL]); exit(1); }
if(v) printf("OK\r\n");
}
if(env_addr) {
if(v) printf("environment: %016" LL "x ", env_addr);
if(!ISHH(env_addr)) { if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: environment %s\r\n",lang[ERR_BADADDR]); exit(1); }
if(env_addr & 4095) { if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: environment %s\r\n",lang[ERR_PAGEALIGN]); exit(1); }
if((env_addr >= mm_addr && env_addr < mm_addr + 16*1024*1024) || (env_addr + 4096 > mm_addr && env_addr + 4096 <= mm_addr + 16*1024*1024))
{ if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: mmio/environment %s\r\n",lang[ERR_ADDRCOL]); exit(1); }
if((env_addr >= fb_addr && env_addr < fb_addr + 16*1024*1024) || (env_addr + 4096 > fb_addr && env_addr + 4096 <= fb_addr + 16*1024*1024))
{ if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: fb/enviroment %s\r\n",lang[ERR_ADDRCOL]); exit(1); }
if(env_addr == bb_addr)
{ if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: bootboot/enviroment %s\r\n",lang[ERR_ADDRCOL]); exit(1); }
if(v) printf("OK\r\n");
}
if(initstack) {
if(v) printf("initstack: %016" LL "x ", initstack);
if(initstack != 1024 && initstack != 2048 && initstack != 4096 && initstack != 8192 && initstack != 16384)
{ if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: initstack %s\r\n",lang[ERR_BADSIZE]); exit(1); }
if(v) printf("OK\r\n");
}
if(v) printf("Load segment: %016" LL "x size %" LL "uK offs %" LL "x ", core_addr, (core_size + bss + 1024)/1024, core_ptr);
if(!ISHH(core_addr)) { if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: segment %s\r\n",lang[ERR_BADADDR]); exit(1); }
if(core_addr & 4095) { if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: segment %s\r\n",lang[ERR_PAGEALIGN]); exit(1); }
if(core_size + bss > 16 * 1024 * 1024) { if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: segment %s\r\n",lang[ERR_BIGSEG]); exit(1); }
if((mm_addr >= core_addr && mm_addr < core_addr + core_size) || (mm_addr + 16*1024*1024 > core_addr && mm_addr + 16*1024*1024 <= core_addr + core_size))
{ if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: mmio/segment %s\r\n",lang[ERR_ADDRCOL]); exit(1); }
if((fb_addr >= core_addr && fb_addr < core_addr + core_size) || (fb_addr + 16*1024*1024 > core_addr && fb_addr + 16*1024*1024 <= core_addr + core_size))
{ if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: fb/segment %s\r\n",lang[ERR_ADDRCOL]); exit(1); }
/* we check for the entrypoint as the lower boundary, because it's okay if the segment is at 0xfffffffe00000 */
if((bb_addr >= entrypoint && bb_addr < core_addr + core_size) || (bb_addr + 4096 > entrypoint && bb_addr + 4096 <= core_addr + core_size))
{ if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: bootboot/segment %s\r\n",lang[ERR_ADDRCOL]); exit(1); }
if((env_addr >= entrypoint && env_addr < core_addr + core_size) || (env_addr + 4096 > entrypoint && env_addr + 4096 <= core_addr + core_size))
{ if(v) { printf("invalid\r\n"); } fprintf(stderr,"mkbootimg: environment/segment %s\r\n",lang[ERR_ADDRCOL]); exit(1); }
if(v) {
if(!mm_addr && !fb_addr && !bb_addr && !env_addr)
printf("OK\r\nComplies with BOOTBOOT Protocol Level 1, %s\r\n",lang[STATADDR]);
else
printf("OK\r\nComplies with BOOTBOOT Protocol Level %s2, %s\r\n",
(!mm_addr || (mm_addr&0xFFFFFFFF)==0xf8000000) && (!fb_addr || (fb_addr&0xFFFFFFFF)==0xfc000000) &&
(!bb_addr || (bb_addr&0xFFFFFFFF)==0xffe00000) && (!env_addr || (env_addr&0xFFFFFFFF)==0xffe01000) &&
((core_addr&0xFFFFFFFF)==0xffe02000) ? "1 and " : "", lang[DYNADDR]);
}
}
/**
* Create a ROM image of the initrd
*/
void makerom()
{
int i, size;
unsigned char *buf, c=0;
FILE *f;
size=((initrd_size[0]+32+511)/512)*512;
if(!initrd_buf[0] || size < 1) { fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_NOINITRD]); exit(1); }
buf=(unsigned char*)malloc(size+1);
if(!buf) { fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_MEM]); exit(1); }
memset(buf, 0, size+1);
/* Option ROM header */
buf[0]=0x55; buf[1]=0xAA; buf[2]=(initrd_size[0]+32+511)/512;
/* asm "xor ax,ax; retf" */
buf[3]=0x31; buf[4]=0xC0; buf[5]=0xCB;
/* identifier, size and data */
memcpy(buf+8,"INITRD",6);
memcpy(buf+16,&initrd_size[0],4);
memcpy(buf+32,initrd_buf[0],initrd_size[0]);
/* checksum */
for(i=0;i<size;i++) c+=buf[i];
buf[6]=(unsigned char)((int)(256-c));
/* write out */
f=fopen("initrd.rom","wb");
if(!f) { fprintf(stderr,"mkbootimg: %s %s\r\n", lang[ERR_WRITE], "initrd.rom"); exit(3); }
fwrite(buf,size,1,f);
fclose(f);
printf("mkbootimg: %s %s.\r\n", "initrd.rom", lang[SAVED]);
}
/**
* Generate an initrd ROM image into a Flashmap image area (section, partition, range whatever)
*/
int flashmapadd(char *file)
{
unsigned char *data=NULL, *desc;
FILE *f;
unsigned int size=0,bs=((initrd_size[0]+511)/512)*512;
/* see if file exists and contains a Flashmap */
if(!file || !*file) return 0;
f=fopen(file,"r");
if(!f) return 0;
fseek(f,0L,SEEK_END);
size=(unsigned int)ftell(f);
fseek(f,0L,SEEK_SET);
data=(unsigned char*)malloc(size + bs);
if(!data) { fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_MEM]); exit(1); }
data[0] = 0; fread(data,1,size,f);
fclose(f);
if(memcmp(data, "__FMAP__", 8)) { free(data); return 0; }
if(!initrd_buf[0] || bs < 1) { fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_NOINITRD]); exit(1); }
/* add a new or replace the last partition descriptor */
desc = data + 0x38 + data[0x36] * 42;
if(!memcmp(desc - 34, "INITRD", 7)) desc -= 42; else data[0x36]++;
size = (*((unsigned int*)(desc - 42)) + *((unsigned int*)(desc - 38)) + 4095) & ~4095;
memset(desc, 0, 42);
memcpy(desc + 0, &size, 4);
memcpy(desc + 4, &bs, 4);
memcpy(desc + 8, "INITRD", 6);
memcpy(data + size,initrd_buf[0],initrd_size[0]);
if(initrd_size[0] < (int)bs) memset(data + size + initrd_size[0], 0, bs - initrd_size[0]);
size += bs; *((unsigned int*)(data + 0x12)) = *((unsigned int*)(data + 0x3c)) = size;
/* write out */
f=fopen(file,"wb");
if(!f) { fprintf(stderr,"mkbootimg: %s %s\r\n", lang[ERR_WRITE], file); exit(3); }
fwrite(data,size,1,f);
fclose(f);
printf("mkbootimg: %s %s.\r\n", file, lang[SAVED]);
return 1;
}
/**
* Main function
*/
int main(int argc, char **argv)
{
Elf64_Ehdr *ehdr;
pe_hdr *pehdr;
int i, j;
unsigned char *data;
char kfn[32768];
FILE *f;
argv = getlang(&argc, argv);
if(argc < 3 || argv[1]==NULL || argv[2] == NULL || !strcmp(argv[1],"help")) {
printf( "BOOTBOOT mkbootimg utility - bztsrc@gitlab\r\n BOOTBOOT Copyright (c) bzt MIT "
"https://gitlab.com/bztsrc/bootboot\r\n%s\r\n"
" Raspbery Pi Firmware Copyright (c) Broadcom Corp, Raspberry Pi (Trading) Ltd\r\n\r\n%s\r\n"
"%s.\r\n\r\n",
deflate_copyright,lang[HELP1],lang[HELP2]);
printf( "%s:\r\n"
" ./mkbootimg check <kernel elf / pe>\r\n"
" ./mkbootimg <%s> initrd.rom\r\n"
" ./mkbootimg <%s> bootpart.bin\r\n"
" ./mkbootimg <%s> <%s>\r\n\r\n",lang[HELP3],lang[HELP4],
lang[HELP4],lang[HELP4],lang[HELP5]);
printf( "%s:\n"
" ./mkbootimg check mykernel/c/mykernel.x86_64.elf\r\n"
" ./mkbootimg myos.json initrd.rom\r\n"
" ./mkbootimg myos.json bootpart.bin\r\n"
" ./mkbootimg myos.json myos.img\r\n",
lang[HELP6]);
return 0;
}
if(!strcmp(argv[1], "check")) {
data = readfileall(argv[2]);
if(!data || read_size < 16) { fprintf(stderr,"mkbootimg: %s %s\r\n",lang[ERR_KRNL],argv[2]); exit(1); }
parsekernel(0, data, 1);
} else {
t = time(NULL);
ts = gmtime(&t);
memset(kfn, 0, sizeof(kfn)); /* <- make valgrind happy with sprintf */
json = (char*)readfileall(argv[1]);
if(!json || !*json) { fprintf(stderr,"mkbootimg: %s %s\r\n",lang[ERR_JSON],argv[1]); exit(1); }
parsejson(json);
parseconfig();
for(i = 0; i < NUMARCH; i++)
if(initrd_dir[i]) {
sprintf(kfn, "%s/%s", initrd_dir[i], kernelname);
data = readfileall(kfn);
if(!data || read_size < 16) { fprintf(stderr,"mkbootimg: %s %s\r\n",lang[ERR_KRNL],kfn); exit(1); }
if(!memcmp(data + 54, "FAT1", 4) || !memcmp(data + 82, "FAT3", 4))
{ fprintf(stderr,"mkbootimg: %s %s\r\n", lang[ERR_BADINITRDTYPE],"FAT"); exit(1); }
parsekernel(i, data, 0);
free(data);
skipbytes = strlen(initrd_dir[i]) + 1;
fs_base = NULL; fs_len = 0; fs_no = 0;
if(rd_open) (*rd_open)(NULL);
parsedir(initrd_dir[i], 0);
if(rd_close) (*rd_close)();
initrdcompress();
initrd_buf[i] = fs_base;
initrd_size[i] = fs_len;
free(initrd_dir[i]);
} else
if(initrd_buf[i]) {
fs_base = initrd_buf[i]; fs_len = initrd_size[i];
if(initrd_buf[i][0] == 0x1f && initrd_buf[i][1] == 0x8b) {
initrduncompress(); initrd_buf[i] = fs_base; initrd_size[i] = fs_len; }
for(j = 0, kfn[0] = 0; j < fs_len - 512; j++) {
ehdr=(Elf64_Ehdr *)(fs_base + j);
pehdr=(pe_hdr*)(fs_base + j + ((mz_hdr*)(fs_base + j))->peaddr);
if(((!memcmp(ehdr->e_ident,ELFMAG,SELFMAG)||!memcmp(ehdr->e_ident,"OS/Z",4)) &&
ehdr->e_ident[EI_CLASS]==ELFCLASS64 && ehdr->e_ident[EI_DATA]==ELFDATA2LSB) ||
(((mz_hdr*)(fs_base + j))->magic==MZ_MAGIC && ((mz_hdr*)(fs_base + j))->peaddr<65536 &&
pehdr->magic == PE_MAGIC && pehdr->file_type == PE_OPT_MAGIC_PE32PLUS)) {
parsekernel(i, fs_base + j, 0);
kfn[0] = 1;
break;
}
}
if(!kfn[0]) { fprintf(stderr,"mkbootimg: %s initrd #%d\r\n",lang[ERR_LOCKRNL],i+1); exit(1); }
if(initrd_gzip) { initrdcompress(); initrd_buf[i] = fs_base; initrd_size[i] = fs_len; }
} else
break;
if(initrd_arch[1] && initrd_arch[1] == initrd_arch[0]) { initrd_size[1] = 0; initrd_arch[1] = 0; }
if(!strcmp(argv[2], "initrd.rom")) makerom(); else
if(!strcmp(argv[2], "initrd.bin")) {
/* write out */
f=fopen("initrd.bin","wb");
if(!f) { fprintf(stderr,"mkbootimg: %s %s\r\n", lang[ERR_WRITE], "initrd.bin"); exit(3); }
fwrite(initrd_buf[0],initrd_size[0],1,f);
fclose(f);
printf("mkbootimg: %s %s.\r\n", "initrd.bin", lang[SAVED]);
} else if(!flashmapadd(argv[2])) {
esp_makepart();
if(!strcmp(argv[2], "bootpart.bin")) {
/* write out */
f=fopen("bootpart.bin","wb");
if(!f) { fprintf(stderr,"mkbootimg: %s %s\r\n", lang[ERR_WRITE], "bootpart.bin"); exit(3); }
fwrite(esp,esp_size,1,f);
fclose(f);
printf("mkbootimg: %s %s.\r\n", "bootpart.bin", lang[SAVED]);
} else {
gpt_maketable();
img_write(argv[2]);
free(gpt);
}
free(esp);
}
free(kernelname);
free(initrd_buf[0]);
if(initrd_buf[1]) free(initrd_buf[1]);
if(initrd_buf[2]) free(initrd_buf[2]);
if(config) free(config);
free(json);
}
return 0;
}