240 lines
4.4 KiB
C
240 lines
4.4 KiB
C
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mount.h>
|
|
#include <fcntl.h>
|
|
#include <sys/wait.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
#include <u.h>
|
|
#include "btld_conf.h"
|
|
#include "btld_disk.h"
|
|
#include "elf32.h"
|
|
|
|
static char *devpath;
|
|
static char *stage1 = "build/biosboot";
|
|
static char *shpath = "build/filecopy.sh";
|
|
static Disk dsk;
|
|
static int btpart;
|
|
static char btcode[512];
|
|
|
|
// need patch
|
|
static struct{
|
|
char *name;
|
|
u32 size;
|
|
u32 addr;
|
|
union{
|
|
u32 val;
|
|
struct{
|
|
u16 low;
|
|
u16 high;
|
|
};
|
|
u8 byte;
|
|
};
|
|
}syms[] = {
|
|
{"start_cluster", 2},
|
|
{"dst_seg_off", 2},
|
|
{"spc_sqrt", 1},
|
|
{"clu_off", 2},
|
|
{"part_offset", 2},
|
|
{"shift_magic", 1},
|
|
{"and_magic", 2},
|
|
};
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
fprintf(stderr, "usgae: ./installboot device [bin]\n");
|
|
exit(1);
|
|
}
|
|
|
|
static int
|
|
streq(char *a, char *b)
|
|
{
|
|
int la = strlen(a);
|
|
return la==strlen(b) && memcmp(a, b, la)==0;
|
|
}
|
|
|
|
static void
|
|
loadelf(char *fname)
|
|
{
|
|
int fd;
|
|
char *tbbeg, *tbstr;
|
|
Ehdr h;
|
|
Ephdr ph;
|
|
Eshdr shsym, shstr;
|
|
Esym *tbsym;
|
|
|
|
assert((fd = open(fname, O_RDONLY))!=-1);
|
|
h = readEhdr(fd);
|
|
assert(h.e_phnum == 1); // only 1 program header
|
|
assert(h.e_shnum >= 2);
|
|
ph = readEphdr(fd, h, 0);
|
|
assert(ph.p_filesz == sizeof(btcode));
|
|
|
|
for(int i = 0; i < h.e_shnum; ++i)
|
|
if((shsym = readEshdr(fd, h, i)).sh_type == SHT_SYMTAB)
|
|
goto found;
|
|
assert(0 && "Can't find shymbol table\n");
|
|
|
|
found:
|
|
shstr = readEshdr(fd, h, shsym.sh_link);
|
|
assert(shstr.sh_type == SHT_STRTAB);
|
|
assert((tbbeg = calloc(1, shstr.sh_size + shsym.sh_size))!=NULL);
|
|
tbstr = readsymtab(fd, shstr, tbbeg);
|
|
tbsym = readsymtab(fd, shsym, tbbeg+shstr.sh_size);
|
|
|
|
for(int i = 0; i < elem(syms); ++i){
|
|
for(int j = 0; j < shsym.sh_size/sizeof(*tbsym); ++j)
|
|
if(streq(syms[i].name, tbstr + tbsym[j].st_name)){
|
|
syms[i].addr = tbsym[j].st_value;
|
|
break;
|
|
}
|
|
assert(syms[i].addr != 0&& "Can't find symbol in symtab\n");
|
|
}
|
|
|
|
readEcode(fd, h, ph, btcode);
|
|
free(tbbeg);
|
|
close(fd);
|
|
}
|
|
|
|
static void
|
|
symset(char *name, u32 val)
|
|
{
|
|
for(int i = 0; i < elem(syms); ++i){
|
|
if(streq(name, syms[i].name)){
|
|
syms[i].val = val;
|
|
return;
|
|
}
|
|
}
|
|
assert(0 && "symbol not exist");
|
|
}
|
|
|
|
static uint
|
|
tou32(u8 *str, int i)
|
|
{
|
|
uint x = 0;
|
|
|
|
do{
|
|
x = x << 8;
|
|
x |= str[--i];
|
|
}while(i > 0);
|
|
return x;
|
|
}
|
|
|
|
static u16
|
|
exponent(u16 x)
|
|
{
|
|
switch(x){
|
|
case 1: return 0;
|
|
case 2: return 1;
|
|
case 4: return 2;
|
|
case 8: return 4;
|
|
case 16: return 8;
|
|
case 32: return 16;
|
|
case 512: return 9;
|
|
case 1024: return 10;
|
|
case 2048: return 11;
|
|
case 4096: return 12;
|
|
default: assert(0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
emit(u64 off)
|
|
{
|
|
for(int i = 0; i < elem(syms); ++i){
|
|
switch(syms[i].size){
|
|
case 1:
|
|
btcode[syms[i].addr] = syms[i].byte;
|
|
break;
|
|
case 2:
|
|
*(u16*)&btcode[syms[i].addr] = syms[i].low;
|
|
break;
|
|
case 4:
|
|
*(u32*)&btcode[syms[i].addr] = syms[i].val;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
assert(pwrite(dsk.fd, btcode+0x3c, sizeof(btcode)-0x3c, off+0x3c) ==
|
|
sizeof(btcode)-0x3c);
|
|
}
|
|
|
|
static void
|
|
setbootparam(int ino, int off, int sec)
|
|
{
|
|
u8 buf[512];
|
|
u16 rdsecsz, dasec;
|
|
|
|
assert(sizeof(buf) == pread(dsk.fd, buf, sizeof(buf), off));
|
|
|
|
u32 bps = tou32(buf+0x0b, 2);
|
|
u32 spc = tou32(buf+0x0d, 1);
|
|
u32 res = tou32(buf+0x0e, 2);
|
|
u32 nfat = tou32(buf+0x10, 1);
|
|
u32 nrde = tou32(buf+0x11, 2);
|
|
// u32 nsec = tou32(buf+0x13, 2);
|
|
u32 nspf = tou32(buf+0x16, 2); // numberof sector per fat16
|
|
rdsecsz = (nrde*32 + (bps - 1))/bps;
|
|
dasec = res + (nspf*nfat) + rdsecsz;
|
|
assert(0xaa55 == tou32(buf+0x1fe, 2));
|
|
|
|
symset("dst_seg_off", bps>>4);
|
|
symset("start_cluster", ino);
|
|
symset("spc_sqrt", exponent(spc));
|
|
symset("clu_off", dasec - 2 * spc);
|
|
symset("part_offset", sec);
|
|
symset("shift_magic", exponent(bps)-3);
|
|
symset("and_magic", bps-1);
|
|
}
|
|
|
|
static void
|
|
install(void)
|
|
{
|
|
char *args[] = {shpath, devpath, NULL};
|
|
pid_t pid;
|
|
u64 beg;
|
|
|
|
switch(pid = fork()){
|
|
default:
|
|
assert(wait(NULL) == pid);
|
|
sync();
|
|
break;
|
|
case 0:
|
|
assert(execvp(args[0], args) != -1);
|
|
case -1:
|
|
assert(0);
|
|
}
|
|
dsk = diskopen(devpath, O_RDWR);
|
|
for(int i = 0; i < elem(dsk.parts); ++i)
|
|
if(dsk.parts[i].type == DOSPTYP_FAT16 && dsk.parts[i].flag == DOSACTIVE){
|
|
btpart = i;
|
|
goto found;
|
|
}
|
|
assert(0);
|
|
|
|
found:
|
|
beg = dsk.parts[btpart].beg * dsk.bps;
|
|
setbootparam(4, beg, dsk.parts[btpart].beg);
|
|
emit(beg);
|
|
close(dsk.fd);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
if(argc < 2 || argc > 3)
|
|
usage();
|
|
devpath = argv[1];
|
|
loadelf(stage1);
|
|
install();
|
|
return 0;
|
|
}
|