From 71c89387fc55152bdc35c64833ff8fb6eda999a1 Mon Sep 17 00:00:00 2001 From: Nero <41307858+nero@users.noreply.github.com> Date: Wed, 3 Feb 2021 00:27:44 +0000 Subject: [PATCH] New bootloader concept: sys-like util to generate a boot sector Right now only floppies are supported --- Makefile | 25 ++--- com/hello.asm | 26 ++--- fix-rom.c => host/fix-rom.c | 0 host/sys.c | 202 ++++++++++++++++++++++++++++++++++++ 4 files changed, 224 insertions(+), 29 deletions(-) rename fix-rom.c => host/fix-rom.c (100%) create mode 100644 host/sys.c diff --git a/Makefile b/Makefile index c13a4e8..fb95607 100644 --- a/Makefile +++ b/Makefile @@ -23,13 +23,16 @@ ifndef DISPLAY QEMU_ARGS += --display curses endif +SYS = host/sys.elf +FIXROM = host/fix-rom.elf + .PHONY: default clean qemu-floppy5 qemu-floppy3 +.PRECIOUS: $(DISTFILES) default: fd1440.img # Host utils -utils/%: %.c - mkdir -p utils +host/%.elf: host/%.c $(CC) -o $@ $< # COM programs @@ -53,26 +56,20 @@ fat6.bs: boot/fat.asm $(NASM) $(NASM_ARGS) -DFAT16 -DCHS -DLARGE -o $@ $< # BIOS option roms -%.rom: rom/%.asm utils/fix-rom - $(NASM) $(NASM_ARGS) -o $@ $< && utils/fix-rom $@ +%.rom: rom/%.asm $(FIXROM) + $(NASM) $(NASM_ARGS) -o $@ $< && $(FIXROM) $@ -fd%.img: fat1.bs $(DISTFILES) - mformat -C -i $@ -f $* -v "$(LABEL)" -B fat1.bs :: +fd%.img: $(DISTFILES) $(SYS) + mformat -C -i $@ -f $* -c 2 -v "$(LABEL)" :: mcopy -i $@ $(DISTFILES) :: - mattrib -i $@ +s ::kernel.bs - -hdimage.img: mbr.bs fd1440.img - cat mbr.bs fd1440.img >$@ + $(SYS) $@ hello.com clean: rm -f *.com *.bs *.0 *.lst *.img *.bin *.rom - rm -rf utils + rm -f host/*.elf qemu-floppy3: fd1440.img $(ROMS) $(QEMU) $(QEMU_ARGS) -boot a -fda fd1440.img qemu-floppy5: fd360.img $(ROMS) $(QEMU) $(QEMU_ARGS) -boot a -fda fd360.img - -qemu-hdd: hdimage.img $(ROMS) - $(QEMU) $(QEMU_ARGS) -boot c -hda hdimage.img diff --git a/com/hello.asm b/com/hello.asm index 30c954c..3e6579b 100644 --- a/com/hello.asm +++ b/com/hello.asm @@ -1,19 +1,15 @@ ; COM program displaying hello world string ; This is primarily for testing purposes -org 0x0100 + org 0x0100 -main: - mov si, string - mov ah, 0x0e - xor bx, bx -.loop: - lodsb - test al, al - jz .end - int 0x10 - jmp .loop -.end: - ret +main: mov si, string + mov ah, 0x0e + xor bx, bx +.loop: lodsb + test al, al + jz .end + int 0x10 + jmp .loop +.end: ret -string: - db "Hello world!", 0x0A, 0x0D, 0 +string: db "Hello world!", 0x0A, 0x0D, 0 diff --git a/fix-rom.c b/host/fix-rom.c similarity index 100% rename from fix-rom.c rename to host/fix-rom.c diff --git a/host/sys.c b/host/sys.c new file mode 100644 index 0000000..d873d33 --- /dev/null +++ b/host/sys.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#define min(a, b) (((a) < (b)) ? (a) : (b)) +// argv[1] must be floppy image +// argv[2] must be name of boot com +// if you expect to see memory safety, close your editor NOW! + +#define AH 4 +#define AX 0 +#define CX 1 +#define DH 6 +#define BX 3 +#define SP 4 +#define SI 6 +#define DI 7 + +char* push_imm8(char* addr, int i) { + *(int8_t*)addr = i; + return(addr+1); +} + +char* push_imm16(char *addr, int i) { + *(int16_t*)addr = i; + return(addr+2); +} + +#define push_mov8(a, b, c) push_imm8(push_imm8(a, 0xB0+b), c); +char* push_mov16(char* a, int b, int c) { + if (c) { + return(push_imm16(push_imm8(a, 0xB8+b), c)); + } else { + // if value is 0 for 16-bit reg, xor it with itself + // saves a byte + return(push_imm16(a, 0xC031 | (b << 11) | (b << 8))); + } +} + +char* push_jc(char* addr, char* target) { + int diff = target - addr; + return(push_imm8(push_imm8(addr, 0x72), diff-2)); +} + +char* push_jmp(char* addr, char* target) { + int diff = target - addr; + if (abs(diff)<0x70) { + return(push_imm8(push_imm8(addr, 0xEB), diff-2)); + } else { + return(push_imm16(push_imm8(addr, 0xE9), diff-3)); + } +} + +int main(int argc, char** argv) { + // query mshowfat for first and last cluster number + // we expect the file to be contiguous + char output[64], cmd[256]= "mshowfat -i "; + strcat(cmd, argv[1]); + strcat(cmd, " '::"); + strcat(cmd, argv[2]); + strcat(cmd, "'"); + FILE* fd = popen(cmd, "r"); + fgets(output, 64, fd); + int r = pclose(fd); + if (r > 0) { + perror("Unable to query mtools"); + exit(1); + } + // parse the two numbers out of the output + // output is like "::kernel.bs <2-4>" + char* p = strchr(output, '<'); + p++; + int min = strtol(p,&p,10); + int max = 0; + if (*p=='-') { + p++; + max = strtol(p,&p,10); + if (*p!='>') { + perror("Unexpected result from mshowfat"); + exit(1); + } + } else { + max = min; + } + if (*p!='>') { + perror("Unexpected result from mshowfat"); + exit(1); + } + fd = fopen(argv[1], "r+"); + if (fd < 0) { + perror("Cant open disk image"); + } + char bootsect[512]; + if (fread(&bootsect, 512, 1, fd) < 1) { + perror("Failed to read bootsector"); + exit(1); + } + // unpack fields from bpb + int ss = *((uint16_t*)(bootsect+0x0B)); + int cs = *((uint8_t*)(bootsect+0x0D)); + int rsc = *((uint16_t*)(bootsect+0x0E)); + int fn = *((uint8_t*)(bootsect+0x10)); + int rde = *((uint16_t*)(bootsect+0x11)); + int fs = *((uint16_t*)(bootsect+0x16)); + int spt = *((uint16_t*)(bootsect+0x18)); + int nos = *((uint16_t*)(bootsect+0x1A)); + + int base = rsc + (fs * fn) + ((rde * 32) / ss); + + int min_s = base + (min - 2) * cs; + int max_s = base + (max - 2) * cs + (cs - 1); + int bx = 0x100; + int es = 0x70; + + char* bs = (char*)&bootsect; + char* ptr = bs+0x3E; + char* putsfunc = ptr; + + // assemble puts function + ptr = push_imm16(ptr, 0xAC5E); + ptr = push_imm16(ptr, 0xC084); + ptr = push_imm16(ptr, 0x0874); + ptr = push_imm16(ptr, 0x0EB4); + ptr = push_imm16(ptr, 0xDB31); + ptr = push_imm16(ptr, 0x10CD); + ptr = push_imm16(ptr, 0xF3EB); + ptr = push_imm16(ptr, 0xE6FF); + + // assemble entry point to our main code + push_jmp(bs, ptr); + + // assemble segment and stack setup + ptr = push_mov16(ptr, AX, es); + ptr = push_imm16(ptr, 0xD08E); // mov sp, ax + ptr = push_mov16(ptr, SP, 0); + + // save two copies of our working segment on the stack + ptr = push_imm8(ptr, 0x50 + AX); // push ax + ptr = push_imm8(ptr, 0x50 + AX); // push ax + + // assemble relocation code + ptr = push_mov16(ptr, AX, 0); + ptr = push_imm16(ptr, 0xD88E); // mov ds, ax + ptr = push_mov16(ptr, SI, 0x7C00); + ptr = push_imm16(ptr, 0xC08E); // mov es, ax + ptr = push_mov16(ptr, DI, (es-0x10) << 4); + ptr = push_mov16(ptr, CX, 0x0100); + ptr = push_imm16(ptr, 0xA5F3); // rep movsw (does the copy) + + ptr = push_imm8(ptr, 0xEA); // jump far + ptr = push_imm16(ptr, ptr+4-bs+0x600); + ptr = push_imm16(ptr, 0); + + // pop DS and ES from the stack + ptr = push_imm8(ptr, 0x07); // pop es + ptr = push_imm8(ptr, 0x1F); // pop ds + + while(min_s <= max_s) { + int sector = (min_s % spt) + 1; + int tmp = min_s / spt; + int head = tmp % nos; + int track = tmp / nos; + int len = min(spt - sector, max_s - min_s)+1; + + // memorize jump address in case of error + char* retry = ptr; + // set up registers for drive reset + ptr = push_imm8(push_imm8(ptr, 0xB0+AH), 0); + ptr = push_imm16(ptr, 0x13CD); + // set up registers for read call + ptr = push_imm16(push_imm8(ptr, 0xB8+AX), 0x0200 | len); + ptr = push_imm16(push_imm8(ptr, 0xB8+CX), sector | (track << 8)); + ptr = push_imm8(push_imm8(ptr, 0xB0+DH), head); + ptr = push_imm16(push_imm8(ptr, 0xB8+BX), bx); + // set up read call + ptr = push_imm16(ptr, 0x13CD); + // jump if carry to retry addr + ptr = push_jc(ptr, retry); + + min_s+=len; + bx=bx+(ss*len); + } + + // emerg return addr + ptr = push_mov16(ptr, AX, 0); + ptr = push_imm8(ptr, 0x50 + AX); + + ptr = push_imm8(ptr, 0xEA); // jmp far + ptr = push_imm16(ptr, 0x100); + ptr = push_imm16(ptr, es); + + // emerg return int 0 + ptr = bs + 0x100; + ptr = push_imm16(ptr, 0x18CD); + + // write back bootsector + fseek(fd, 0, SEEK_SET); + if (fwrite(&bootsect, 512, 1, fd) < 1) { + perror("Failed to write back boot sector"); + exit(1); + } +}