commit 3f556cba199ef6ee6b609bb1440a6c29288ee19a Author: Hojun-Cho Date: Wed Nov 27 17:06:47 2024 +0900 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6127b3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1671a58 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +BDIR = build +SUBDIRS = biosboot boot mbr installboot installmbr + +all: $(BDIR) $(SUBDIRS) + +$(BDIR): + mkdir -p $(BDIR) + +clean: $(SUBDIRS) + rm -rf $(BDIR) + +$(SUBDIRS): + @make -sC $@ $(MAKECMDGOALS) BDIR=$(PWD)/$(BDIR) IDIR=$(PWD) + +.PHONY: biosboot boot installboot mbr installmbr diff --git a/Makefile.inc b/Makefile.inc new file mode 100644 index 0000000..25ce203 --- /dev/null +++ b/Makefile.inc @@ -0,0 +1,12 @@ +LINKADDR = 0x40120 +LOADADDR = 0x40000 +BOOTMAGIC = 0xc001d00d +BOOTREL = 0x60000 + +CC=cc +CFLAGS=-m32 -Wall -Werror -march=i386 -O0 -fno-stack-protector -ffreestanding -fno-builtin +CPPFLAGS=-nostdinc +LD=ld +AS=as +ASFLAGS=-Wa,-O0,--32 +LDFLAGS=-melf_i386 -O0 -nostdlib -x -N -Bstatic -znorelro -no-pie diff --git a/README.md b/README.md new file mode 100644 index 0000000..24365d0 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +### MBR bootloader + diff --git a/biosboot/Makefile b/biosboot/Makefile new file mode 100644 index 0000000..e16529e --- /dev/null +++ b/biosboot/Makefile @@ -0,0 +1,18 @@ +include ../*.inc + +PROG=biosboot +SRCS=$(wildcard *.S) +OBJS=$(SRCS:.S=.o) + +CFLAGS+=-c -fno-pie +LDFLAGS+=-Ttext 0 -T ld.script +CPPFLAGS+=-nostdinc -DLOADADDR=$(LOADADDR) -DLINKADDR=$(LINKADDR) -DBOOTMAGIC=$(BOOTMAGIC) +CPPFLAGS+=-I $(IDIR) + +$(PROG): $(OBJS) + $(LD) $(LDFLAGS) -o $(BDIR)/$(PROG) $(OBJS) + +$(OBJS): ../def.h + +clean: + rm -f $(PROG) $(OBJS) diff --git a/biosboot/biosboot.S b/biosboot/biosboot.S new file mode 100644 index 0000000..3721988 --- /dev/null +++ b/biosboot/biosboot.S @@ -0,0 +1,203 @@ +.file "biosboot.S" + +#include "def.h" + +// 0x00000 -> 0x07BFF our stack (to 31k) +// 0x07A00 -> 0x07BFF typical MBR loc (at 30k5) +// 0x07C00 -> 0x07DFF our code biosboot (at 31k) +// 0x07E00 -> ... /boot inode block (at 31k5) +// 0x07E00 -> ... (indirect block if nec) +// 0x40000 -> ... /boot (at 256k) + +#define PBR_READ_ERROR 'R' +#define PBR_CANT_BOOT 'X' +#define PBR_BAD_MAGIC 'M' +#define PBR_TOO_MANY_INDIRECTS 'I' +#define CHAR_BLOCK_READ '.' +#define putc(c) movb $c, %al; call Lchr +#define puts(s) movw $s, %si; call Lmessage + + .globl start_cluster, dst_seg_off + .globl spc_sqrt, clu_off + .globl part_offset, shift_magic, and_magic + + .type start_cluster, @function + .type dst_seg_off, @function + .type spc_sqrt, @function + .type clu_off, @function + .type part_offset, @function + .type shift_magic, @function + .type and_magic, @function + +.text +.code16 +.globl _start + +_start: + jmp begin + nop + + . = _start + 0x1c +ebpb: .long 16 /* hidden sectors */ + .long 0 /* large sectors */ + .word 0 /* physical disk */ + .byte 0x29 /* signature, needed by NT */ + .space 4, 0 /* volume serial number */ + .asciz "YO LABEL" + .asciz "FAT 16" + + + . = _start + 0x3e +begin: + ljmp $BOOTSEG, $main + +cant_boot: + movb $PBR_CANT_BOOT, %al + jmp err_print_crlf + +main: + xorw %ax, %ax + movw %ax, %ss + movw $BOOTSTACKOFF, %sp + +// data segment == code segment + pushw %cs + popw %ds + movw $load_msg, %si + call Lmessage + testb $0x80, %dl + jz cant_boot + +load_boot: + start_cluster = .+1 + movw $0x9090, %ax + movw $LOADSEG, %bx // destination + movw %bx, %di + movw $FATSEG, %cx + movw %cx, %es +1: + call read_cluster + + read_fat: + pushw %ax + shift_magic = .+1 + movb $0x90, %cl + shrw %cl, %ax + addw %ds:0x0e, %ax + movw $FATSEG, %bx + call read_sector + popw %cx + and_magic = .+2 + andw $0x9090, %cx + movw %es:(,%ecx, 2), %ax + cmpw $0xFFF7, %ax + jb 1b + + jmp done_load + +read_cluster: + pushw %ax + + spc_sqrt = .+2 + shlw $0x90, %ax + clu_off = .+1 + addw $0x9090, %ax + + movzb %ds:0x0d, %cx +1: + call read_sector + incw %ax + dst_seg_off = .+2 + addw $0x9090, %bx + loop 1b + + popw %ax + ret + +read_sector: + pushw %ax + movw %bx, lba_dst_seg + part_offset = .+1 + addw $0x9090, %ax + movl %eax, lba_src_low + movw $lba_command, %si + +do_int_13: + movb $0x42, %ah + int $0x13 + jc read_error + + popw %ax + ret + +read_error: + movb $PBR_READ_ERROR, %al +err_print_crlf: + movw $err_txt_crlf, %si + jmp err_print2 + call stay_stopped +Lmessage: + cld +1: + lodsb + orb %al, %al + jz 1f + call Lchr + jmp 1b + +Lchr: + pushw %bx + movb $0x0e, %ah + xorw %bx, %bx + incw %bx + int $0x10 + popw %bx +1: + ret + +done_load: + movw $LOADSEG, %ax + movw %ax, %es + cmpl $ELFMAGIC, %es:0(,1) + je exec_boot + movb $PBR_BAD_MAGIC, %al + +err_print: + movw $err_txt, %si +err_print2: + movb %al, err_id +err_stop: + call Lmessage +stay_stopped: + sti + hlt + jmp stay_stopped + +exec_boot: + movzbl %dl, %eax + pushl %eax + pushl $BOOTMAGIC + ljmp $(LINKADDR >> 4), $0 + +lba_command: + .byte 0x10 /* size of command packet */ + .byte 0x00 /* reserved */ +lba_nread_sec: + .word 0x01 // sectors to transfer max 127 +lba_dst_offset: + .word 0x00 // target buffer offset +lba_dst_seg: + .word 0x00 // target buffer offset +lba_src_low: + .long 0x00 +lba_src_high: + .long 0x00 + +load_msg: .asciz "Loading" +err_txt_crlf: .ascii "\r\n" +err_txt: .ascii "ERR " +err_id: .ascii "?" +crlf: .asciz "\r\n" + +. = 0x200 - 2 + .word DOSMBR_SIGNATURE diff --git a/biosboot/ld.script b/biosboot/ld.script new file mode 100644 index 0000000..71f1a9c --- /dev/null +++ b/biosboot/ld.script @@ -0,0 +1,17 @@ +PHDRS +{ + text PT_LOAD; +} + +SECTIONS +{ + .text : + { + *(.text) + } :text + /DISCARD/ : + { + *(.note.gnu.property) + *(.comment) + } +} diff --git a/boot/Makefile b/boot/Makefile new file mode 100644 index 0000000..975cc98 --- /dev/null +++ b/boot/Makefile @@ -0,0 +1,17 @@ +include ../*.inc + +PROG=boot +CSRCS= $(wildcard *.c) +SSRCS= $(wildcard *.S) +OBJS = $(SSRCS:.S=.o) $(CSRCS:.c=.o) + +CFLAGS+=-c -fno-pie +CPPFLAGS+=-nostdinc -DLOADADDR=$(LOADADDR) -DLINKADDR=$(LINKADDR) -DBOOTMAGIC=$(BOOTMAGIC) +LDFLAGS+= -s -T ld.script -Ttext=$(LINKADDR) --no-omagic + +${PROG}: $(OBJS) + @rm -f $(PROG) + $(LD) $(LDFLAGS) -o $(BDIR)/$(PROG) $(OBJS) + +clean: + rm -f $(PROG) $(PROG).bin $(OBJS) diff --git a/boot/boot.c b/boot/boot.c new file mode 100644 index 0000000..dad5c78 --- /dev/null +++ b/boot/boot.c @@ -0,0 +1,56 @@ +typedef int dev_t; + +#define SCREEN_WIDTH 80 +#define SCREEN_LENGTH 25 +#define VGA_BUFFER_ADDR 0xb8000 + +char* +memcpy(char *a, char *b, int l) +{ + for(int i = 0; i < l; ++i) + a[i] = b[i]; + return a; +} + +int +strlen(char *s) +{ + int i = 0; + for(; s[i]; ++i) + ; + return i; +} + +char* +itostr(int n, char *s) +{ + char buf[16]; + char *p = buf; + + do { + *p++ = n%16; + }while((n/=16) != 0); + + while(p > buf){ + *s++ = "0123456789abcdef"[(int)*--p]; + } + return s; +} + +void +boot(dev_t bootdev) +{ + char *p; + char s[SCREEN_WIDTH] = "booted on disk:0x"; + short* vga = (short*)VGA_BUFFER_ADDR; + + p = s+strlen(s); + p = itostr((int)bootdev, p); + for(int j = 0; j < SCREEN_LENGTH; ++j){ + for (int i = 0; i < sizeof(s); ++i) { + vga[(j*SCREEN_WIDTH) + (i + SCREEN_WIDTH*2)] = 0x9100 | s[i]; + } + } + while(1) + ; +} diff --git a/boot/gdt.S b/boot/gdt.S new file mode 100644 index 0000000..cec9788 --- /dev/null +++ b/boot/gdt.S @@ -0,0 +1,74 @@ +.file "gdt.S" + +/* memory segment types */ +#define SDT_MEMRO 16 /* memory read only */ +#define SDT_MEMROA 17 /* memory read only accessed */ +#define SDT_MEMRW 18 /* memory read write */ +#define SDT_MEMRWA 19 /* memory read write accessed */ +#define SDT_MEMROD 20 /* memory read only expand dwn limit */ +#define SDT_MEMRODA 21 /* memory read only expand dwn limit accessed */ +#define SDT_MEMRWD 22 /* memory read write expand dwn limit */ +#define SDT_MEMRWDA 23 /* memory read write expand dwn limit acessed */ +#define SDT_MEME 24 /* memory execute only */ +#define SDT_MEMEA 25 /* memory execute only accessed */ +#define SDT_MEMER 26 /* memory execute read */ +#define SDT_MEMERA 27 /* memory execute read accessed */ +#define SDT_MEMEC 28 /* memory execute only conforming */ +#define SDT_MEMEAC 29 /* memory execute only accessed conforming */ +#define SDT_MEMERC 30 /* memory execute read conforming */ +#define SDT_MEMERAC 31 /* memory execute read accessed conforming */ + +#ifndef _ALIGN_TEXT +# define _ALIGN_TEXT .align 2, 0x120 +#endif + +/* NB == No Binding: use .globl or .weak as necessary */ +#define _ENTRY_NB(x) \ + .text; _ALIGN_TEXT; .type x,@function; x: +#define _ENTRY(x) .globl x; _ENTRY_NB(x) + + +.text +.code32 + + .align 8 +gdt: + /* 0x00 : null */ + .space 8 + /* 0x08 : flat code */ + .word 0xFFFF # lolimit + .word 0 # lobase + .byte 0 # midbase + .byte SDT_MEMERAC | 0 | 0x80 # RXAC, dpl = 0, present + .byte 0xf | 0 | 0x40 | 0x80 # hilimit, xx, 32bit, 4k granularity + .byte 0 # hibase + /* 0x10 : flat data */ + .word 0xFFFF # lolimit + .word 0 # lobase + .byte 0 # midbase + .byte SDT_MEMRWA | 0 | 0x80 # RWA, dpl = 0, present + .byte 0xf | 0 | 0x40 | 0x80 # hilimit, xx, 32bit, 4k granularity + .byte 0 # hibase + /* 0x18 : 16 bit code */ + .word 0xFFFF # lolimit + .word (LINKADDR & 0xffff) # lobase + .byte (LINKADDR >> 16) & 0xff # midbase + .byte SDT_MEMERAC | 0 | 0x80 # RXAC, dpl = 0, present + .byte 0x0 | 0 | 0 | 0 # hilimit, xx, 16bit, byte granularity + .byte (LINKADDR >> 20) & 0xff # hibase + /* 0x20 : 16 bit data */ + .word 0xFFFF # lolimit + .word (LINKADDR & 0xffff) # lobase + .byte (LINKADDR >> 16) & 0xff # midbase + .byte SDT_MEMRWA | 0 | 0x80 # RWA, dpl = 0, present + .byte 0x0 | 0 | 0 | 0 # hilimit, xx, 16bit, byte granularity + .byte (LINKADDR >> 20) & 0xff # hibase + + +.globl Gdtr +Gdtr: + .word . - gdt -1 + .long gdt + .word 0 + +.end diff --git a/boot/ld.script b/boot/ld.script new file mode 100644 index 0000000..48ca7ae --- /dev/null +++ b/boot/ld.script @@ -0,0 +1,26 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +PHDRS +{ + text PT_LOAD; +} +SECTIONS +{ + .text : { + KEEP(srt0.o(.text)) + *(.text) + } :text + etext = .; + .data : { *(.data) } :text + edata = .; + .bss : { *(.bss) } :text + ebss = .; + end = .; + /DISCARD/ : + { + *(.note.gnu.property) + *(.comment) + } +} + diff --git a/boot/srt0.S b/boot/srt0.S new file mode 100644 index 0000000..23ef2d0 --- /dev/null +++ b/boot/srt0.S @@ -0,0 +1,47 @@ +.file "srt0.S" + +#define BOOTSTACK 0xfffc +#define CR0_PE 0x00000001 /* Protected mode Enable */ + + .globl end + .globl edata + .globl Gdtr + .globl boot + + .text + .code16 + .align 16 + .globl _start +_start: + popl %eax + cmpl $BOOTMAGIC, %eax + je 1f + +1: + popl %edx + cli + pushl %cs + popl %ds + addr32 data32 lgdt (Gdtr - LINKADDR) + movl %cr0, %eax + orl $CR0_PE, %eax + data32 movl %eax, %cr0 + data32 ljmp $8, $1f + +1: + .code32 + movl $0x10, %eax + mov %ax, %ds + mov %ax, %ss + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + movl $BOOTSTACK, %esp + pushl %edx + + + // fill 0 .bss + xorl %eax, %eax + + call boot; + diff --git a/dat.h b/dat.h new file mode 100644 index 0000000..85769d7 --- /dev/null +++ b/dat.h @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "def.h" + +#define elem(x) ((int)(sizeof(x)/sizeof((x)[0]))) + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef unsigned int uint; + +typedef struct{ + u64 cyl; + u64 head; + u64 sec; +}CHS; + +typedef struct{ + u8 flag; /* bootstrap flags */ + u8 bhd; /* begin head */ + u8 bsec; /* begin sector */ + u8 bcyl; /* begin cylinder */ + u8 type; /* partition type (see below) */ + u8 ehd; /* end head */ + u8 esec; /* end sec2r */ + u8 ecyl; /* end cylinder */ + u32 beg; /* absolute starting sectoff number */ + u32 size; /* partition size in sec2rs */ +} __attribute__((packed)) DOSpart; + +typedef struct{ + u8 boot[DOSPARTOFF]; + DOSpart parts[NDOSPART]; + u16 sign; +} __attribute__((packed)) DOSmbr; + +typedef struct +{ + u64 bs, ns; + u8 flag, id; +}Part; + +typedef struct +{ + DOSpart parts[NDOSPART]; + u32 bps; // byte per sector + u32 spt; // sector per track + u32 hpc; // header per cylinder + u32 nsec; // number of sectors; + u32 type; // major minor; + int fd; + char *name; +}Disk; + +typedef struct +{ + u32 ext; + u32 self; + u8 code[DOSPARTOFF]; + Part parts[NDOSPART]; + u16 sign; + u16 zeros; +}MBR; + +static Disk +diskopen(char *name, int flag) +{ + char buf[512]; + u64 nsec; + struct stat st; + Disk d = {.bps=512, .spt=63, .hpc=255,}; + + d.name = name; + assert((d.fd = open(d.name, flag)) != -1); + assert(fstat(d.fd, &st) != -1); + if(S_ISBLK(st.st_mode)) + assert(ioctl(d.fd, BLKGETSIZE, &nsec) != -1); + else if(S_ISREG(st.st_mode)) + nsec = st.st_size/d.bps; + else + assert(0); + + d.nsec = nsec < FAT16_MAX ? nsec : FAT16_MAX; + assert(sizeof(buf) == read(d.fd, buf, sizeof(buf))); + memcpy(d.parts, buf+DOSPARTOFF, sizeof(d.parts)); + assert(lseek(d.fd, 0, SEEK_SET) == 0); + + return d; +} diff --git a/def.h b/def.h new file mode 100644 index 0000000..6dbd9a7 --- /dev/null +++ b/def.h @@ -0,0 +1,22 @@ +#define FAT16_MAX (0x40000) +#define DOSPTYP_UNUSED 0x00 +#define DOSPTYP_FAT16 0x06 +#define DOSPARTOFF 446 +#define DOSDISKOFF 444 +#define NDOSPART 4 +#define DOSACTIVE 0x80 +#define DOSMBR_SIGNATURE 0xAA55 +#define BOOT_MAGIC 0xf1abde3f +#define DOSMBR_SIGNATURE_OFF 0x1fe + +#define BOOTSEG 0x7c0 +#define BOOTRELOCSEG 0x7a0 +#define MBRSTACKOFF 0xfffc +#define PARTSZ 16 +#define LOADSEG (LOADADDR >> 4) +#define FATSEG 0x07e0 /* FAT table segment */ + +#define BOOTSTACKOFF ((BOOTSEG << 4) - 4) /* stack starts here, grows down */ +#define LFMAGIC 0x464c /* LFMAGIC (last two bytes of \7fELF) */ +#define ELFMAGIC 0x464c457f /* ELFMAGIC ("\7fELF") */ + diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..7d78f9d --- /dev/null +++ b/install.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +S=$(pwd)/build +img=$1 + +set -e + +make + +if [ -z ${1} ] + then + img=$S/img + rm -rf $img + dd if=/dev/zero of=$img bs=512 count=262144 +fi + +sudo ${S}/installmbr $img +sudo ${S}/installboot $img diff --git a/installboot/Makefile b/installboot/Makefile new file mode 100644 index 0000000..f54fed8 --- /dev/null +++ b/installboot/Makefile @@ -0,0 +1,15 @@ +PROG=installboot +SRCS=$(wildcard *.c) +OBJS=$(SRCS:.c=.o) + +CFLAGS = -g -Wall -Werror +CPPFLAGS = -I $(IDIR) + +${PROG}: $(OBJS) + $(CC) $(CFLAGS) -o $(BDIR)/$(PROG) $^ + cp filecopy.sh $(BDIR) + +$(OBJS): ../dat.h + +clean: + rm -rf $(PROG) *.o diff --git a/installboot/elf32.h b/installboot/elf32.h new file mode 100644 index 0000000..4fba22c --- /dev/null +++ b/installboot/elf32.h @@ -0,0 +1,65 @@ +#include + +/* e_ident */ +#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \ + (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \ + (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \ + (ehdr).e_ident[EI_MAG3] == ELFMAG3) + +typedef Elf32_Ehdr Ehdr; +typedef Elf32_Phdr Ephdr; +typedef Elf32_Shdr Eshdr; +typedef Elf32_Sym Esym; + +static Ehdr +readEhdr(int fd) +{ + Ehdr h; + + assert(pread(fd, &h, sizeof(h), 0) == h.e_ehsize); + assert(IS_ELF(h) == 1); + return h; +} + +static Ephdr +readEphdr(int fd, Ehdr h, int i) +{ + Ephdr ph; + int off; + + off = h.e_phoff+i*h.e_phentsize; + assert(pread(fd, &ph, h.e_phentsize, off) + == sizeof(Ephdr)); + return ph; +} + +static Eshdr +readEshdr(int fd, Ehdr h, int i) +{ + Eshdr sh; + int off; + + off = h.e_shoff + i*h.e_shentsize; + assert(pread(fd, &sh, h.e_shentsize, off) == sizeof(sh)); + return sh; +} + +static void* +readEcode(int fd, Ehdr h, Ephdr ph, void *dst) +{ + if(dst == NULL) + assert((dst = calloc(1, ph.p_filesz)) != NULL); + assert(pread(fd, dst, ph.p_filesz, ph.p_offset) == + ph.p_filesz); + return dst; +} + +static void* +readsymtab(int fd, Eshdr sh, void *dst) +{ + if(dst == NULL) + assert((dst = calloc(1, sh.sh_size)) != NULL); + assert(pread(fd, dst, sh.sh_size, sh.sh_offset) + == sh.sh_size); + return dst; +} diff --git a/installboot/filecopy.sh b/installboot/filecopy.sh new file mode 100755 index 0000000..cd381ea --- /dev/null +++ b/installboot/filecopy.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +abort() +{ + umount $dir 2>/dev/null + rm -rf $dir + echo >&2 "fail to install" + exit 0 +} + +trap 'abort' 0 +set -eu + +dir=$(mktemp -d) +nsec=$((($(blockdev --getsz $1) - 64)/2)) + +if [ ${nsec} -ge $((16*1024)) ] + then + nsec=$((16*1024-64/2)) +fi + +mkfs.fat -F16 -R 1 -r 512 -a -I --offset 64 $1 $nsec + +mount -o offset=$((512*64)) $1 $dir + +mkdir -p $dir/root + +cp -f build/boot $dir/root/boot + +trap - 0 +umount $dir >&2 +rm -rf $dir +sync +exit 1 diff --git a/installboot/installboot.c b/installboot/installboot.c new file mode 100644 index 0000000..13875eb --- /dev/null +++ b/installboot/installboot.c @@ -0,0 +1,244 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "dat.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}, +}; + +void +usage(void) +{ + fprintf(stderr, "usgae: ./installboot device [bin]\n"); + exit(1); +} + +int +streq(char *a, char *b) +{ + int la = strlen(a); + return la==strlen(b) && memcmp(a, b, la)==0; +} + +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); +} + +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"); +} + +uint +tou32(u8 *str, int i) +{ + uint x = 0; + + do{ + x = x << 8; + x |= str[--i]; + }while(i > 0); + return x; +} + +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); + } +} + +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); +} + +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); +} + +void +catpath(char *dst, char *s1, char *s2) +{ + while(*s1) + *dst++ = *s1++; + *dst++ = '/'; + while(*s2) + *dst++ = *s2++; + *dst = '\0'; +} + +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; +} diff --git a/installmbr/Makefile b/installmbr/Makefile new file mode 100644 index 0000000..982e7d7 --- /dev/null +++ b/installmbr/Makefile @@ -0,0 +1,15 @@ +PROG=installmbr +SRCS=$(wildcard *.c) +OBJS=$(SRCS:.c=.o) + +CFLAGS = -g +CPPFLAGS = -I $(IDIR) + +${PROG}: $(OBJS) + @rm -f $(PROG) + $(CC) $(CFLAGS) -o $(BDIR)/$(PROG) $^ + +$(OBJS): ../dat.h + +clean: + rm -rf $(PROG) $(OBJS) diff --git a/installmbr/installmbr.c b/installmbr/installmbr.c new file mode 100644 index 0000000..d32b10e --- /dev/null +++ b/installmbr/installmbr.c @@ -0,0 +1,238 @@ +#include +#include +#include +#include +#include +#include +#include +#include "dat.h" + +static char *binpath = "build/mbr"; +static char bootcode[DOSPARTOFF]; +static Disk dsk; + +void +usage(void) +{ + fprintf(stderr, "usage: ./mkmbr device\n"); + exit(1); +} + +static int +secwrite(void *buf, u64 sec, u32 cnt, Disk *dsk) +{ + ssize_t wc; + off_t off; + ssize_t n = cnt * dsk->bps; + off_t where = sec * dsk->bps; + + off = lseek(dsk->fd, where, SEEK_SET); + assert(off != -1); + wc = write(dsk->fd, buf, n); + assert(wc != -1); + assert(wc == n); + return wc; +} + +char* +secread(u64 sec, u32 cnt, Disk *dsk) +{ + char *buf; + ssize_t rc; + off_t off; + ssize_t n = cnt * dsk->bps; + off_t where = sec * dsk->bps; + + off = lseek(dsk->fd, where, SEEK_SET); + assert(off != -1); + buf = calloc(1, n); + assert(buf != NULL); + rc = read(dsk->fd, buf, n); + assert(rc != -1); + assert(rc == n); + return buf; +} + +int +diskwrite(void *buf, u64 sec, u64 sz, Disk *dsk) +{ + char *secbuf; + u32 cnt; + int wc; + + cnt = (sz + dsk->bps - 1) / dsk->bps; + secbuf = secread(sec, cnt, dsk); + memcpy(secbuf, buf, sz); + wc = secwrite(secbuf, sec, cnt, dsk); + free(secbuf); + return wc; +} + +Part +dospt2pt(DOSpart dpt, u64 self, u64 ext) +{ + Part pt={0,}; + off_t off; + u32 t; + + assert(dpt.type != DOSPTYP_FAT16); + pt.flag = dpt.flag; + pt.id = dpt.type; + off = self; + memcpy(&t, &dpt.beg, sizeof(u32)); + pt.bs = htole32(t) + off; + memcpy(&t, &dpt.size, sizeof(u32)); + pt.ns = htole32(t); + return pt; +} + +// C = LBA / (HPC * SPT) +// H = (LBA / SPT) % HPC +// S = (LBA / SPT) + 1 + +CHS +lba2chs(u64 lba, u64 spt, u64 hpc) +{ + CHS c = {0,}; + + c.cyl = lba / (spt * hpc); + c.head = (lba / spt) % hpc; + c.sec = (lba % spt) + 1; + return c; +} + +CHS +lba2chsbeg(Part pt) +{ + if(pt.ns == 0 || pt.id == DOSPTYP_UNUSED) + return (CHS){0,}; + return lba2chs(pt.bs, dsk.spt, dsk.hpc); +} + +CHS +lba2chsend(Part pt) +{ + if(pt.ns == 0 || pt.id == DOSPTYP_UNUSED) + return (CHS){0,}; + return lba2chs(pt.bs+pt.ns-1, dsk.spt, dsk.hpc); +} + +CHS +chsnorm(u8 id, CHS c) +{ + if(c.head > 254 || c.sec > 63 || c.cyl > 1023){ + c.head = 254; + c.sec = 63; + c.cyl = 1023; + } + c.head = c.head & 0xFF; + c.sec = (c.sec & 0x3F) | ((c.cyl & 0x300) >> 2); + c.cyl = c.cyl & 0xFF; + return c; +} + +DOSpart +pt2dospt(Part pt, u64 self, u64 ext) +{ + DOSpart d={0,}; + CHS c; + u64 off, t; + + if(pt.ns == 0 || pt.id == DOSPTYP_UNUSED) + return d; + off = self; + c = chsnorm(pt.id, lba2chsbeg(pt)); + d.bcyl = c.cyl; + d.bhd = c.head; + d.bsec = c.sec; + c = chsnorm(pt.id, lba2chsend(pt)); + d.ecyl = c.cyl; + d.ehd = c.head; + d.esec = c.sec; + d.flag = pt.flag & 0xff; + d.type = pt.id & 0xff; + t = pt.bs - off; + memcpy(&d.beg, &t, sizeof(d.beg)); + t = htole64(pt.ns); + memcpy(&d.size, &t, sizeof(d.size)); + return d; +} + +DOSmbr +mbr2dosmbr(MBR m) +{ + DOSmbr d={0,}; + + memcpy(d.boot, m.code, sizeof(m.code)); + d.sign = htole16(DOSMBR_SIGNATURE); + for(int i = 0; i < elem(d.parts); ++i){ + DOSpart dpt = {0,}; + if(i < elem(m.parts)) + dpt = pt2dospt(m.parts[i], m.self, m.ext); + d.parts[i] = dpt; + } + return d; +} + +MBR +mbrinit(void) +{ + MBR m = {0,}; + DOSpart dpt={0,}; + Part bpt={0,}, mpt={0,}; + u32 i; + + bpt = dospt2pt(dpt, 0, 0); + i = 1; + while(i < dsk.spt) + i *= 2; + mpt.bs = i; + mpt.ns = dsk.nsec - mpt.bs; + mpt.id = DOSPTYP_FAT16; + mpt.flag = DOSACTIVE; + + memcpy(m.code, bootcode, sizeof(bootcode)); + m.parts[0] = bpt; + m.parts[3] = mpt; + m.sign = DOSMBR_SIGNATURE; + return m; +} + +int +mbrwrite(MBR m) +{ + DOSmbr d; + int wc; + + d = mbr2dosmbr(m); + wc = diskwrite(&d, m.self, sizeof(d), &dsk); + return wc; +} + +void +read512(char *path) +{ + int fd; + ssize_t rc; + + assert((fd = open(path, O_RDONLY)) != -1); + rc = read(fd, bootcode, sizeof(bootcode)); + assert(rc == sizeof(bootcode)); + close(fd); +} + +int +main(int argc, char *argv[]) +{ + MBR m; + char buf[1024]; + + printf("%s\n", getcwd(buf, sizeof(buf))); + if(argc < 2) + usage(); + read512(binpath); + dsk = diskopen(argv[1], O_RDWR); + m = mbrinit(); + printf("write %dbyte to %s\n", mbrwrite(m), dsk.name); + return 0; +} diff --git a/mbr/Makefile b/mbr/Makefile new file mode 100644 index 0000000..1132e93 --- /dev/null +++ b/mbr/Makefile @@ -0,0 +1,19 @@ +PROG=mbr +SRCS=$(wildcard *.S) +OBJS=$(SRCS:.S=.o) + +LDFLAGS = -Ttext 0 -e 0 +CPPFLAGS = -I $(IDIR) + +${PROG}: $(OBJS) + @rm -f $(PROG) + $(LD) $(LDFLAGS) -o $(PROG) $(OBJS) + objcopy -O binary mbr mbr.new + dd if=mbr.new of=mbr bs=512 count=1 + rm -rf mbr.new + mv $(PROG) $(BDIR) + +$(OBJS): ../def.h + +clean: + rm -f *.o *.bin $(PROG) diff --git a/mbr/mbr.S b/mbr/mbr.S new file mode 100644 index 0000000..bdc08aa --- /dev/null +++ b/mbr/mbr.S @@ -0,0 +1,193 @@ +.file "mbr.S" + +#include "def.h" + +#define CHAR_LBA_READ '.' +#define CHAR_R 'R' +#define CHAR_S 'S' +#define CHAR_L 'L' +#define CHAR_B 'B' +#define CHAR_G 'G' +#define DBGMSG(c) movb $c, %al; call Lchr +#define puts(s) movw $s, %si; call Lmessage + +// 0x07C00 - 0x07DFF BIOS load us here +// 0x07E00 - 0x17BFC using stack +// 0x07A00 - 0x07BFF relocate to here +// 0x07C00 - 0x07DFF load PBR here + +.text +.code16 +.globl start + +start: + // Adjust %cs to be right + ljmp $BOOTSEG, $1f +1: + // set up stack + movw %cs, %ax + // cli + movw %ax, %ss + movw $MBRSTACKOFF, %sp + + // set up data segment + movw %ax, %ds + DBGMSG(CHAR_S) + + // relocate for PBR + // copy to 0x07a0 + movw $BOOTRELOCSEG, %ax + movw %ax, %es + xorw %si, %si + xorw %di, %di + movw $0x200, %cx // Move (E)CX words from DS:[(E)SI] to ES:[(E)DI] + cld + rep movsb + + // jump to relocated self + ljmp $BOOTRELOCSEG, $reloc + +reloc: + DBGMSG(CHAR_R) + + pushw %ds + popw %es + pushw %cs + popw %ds + testb $DOSACTIVE, %dl + jnz drive_ok + puts(efdmbr) + jmp stay_stopped + +drive_ok: + movw $pt, %si + movw $NDOSPART, %cx + +find_active: + DBGMSG(CHAR_L) + movb (%si), %al + cmpb $DOSACTIVE, %al + je found + addw $PARTSZ, %si + loop find_active + +no_part: + movw $enoboot, %si + +err_stop: + call Lmessage + +stay_stopped: + sti + hlt + jmp stay_stopped + +found: + DBGMSG(CHAR_B) + movb %dl, %al + andb $0x0f, %al + addb $'0', %al + movb %al, drive_num + + movb $'0'+4, %al + subb %cl, %al + movb %al, part_num + pushw %si + movw $info, %si + call Lmessage + popw %si + movw $0, %es:signature(,1) + movb %dl, (%si) + movw $0x55AA, %bx + movb $0x41, %ah + int $0x13 + + movb (%si), %dl + jc read_error + cmpw $0xAA55, %bx + jne read_error + testb $0x01, %cl + jz read_error + +do_lba: + movb $CHAR_LBA_READ, %al + call Lchr + movl 8(%si), %ecx + movl %ecx, lba_sector + + pushw %si + movb $0x42, %ah + movw $lba_command, %si + int $0x13 + popw %si + jnc booting_os + +read_error: + movw $eread, %si + jmp err_stop + +booting_os: + puts(crlf) + + cmpw $DOSMBR_SIGNATURE, %es:signature(,1) + jne missing_os + ljmp $0, $BOOTSEG << 4 + +missing_os: + movw $enoos, %si + jmp err_stop + +Lmessage: + pushw %ax + cld +1: + lodsb + testb %al, %al + jz 1f + call Lchr + jmp 1b + +Lchr: + pushw %ax + pushw %bx + movb $0x0e, %ah + movw $1, %bx + int $0x10 + popw %bx +1: popw %ax + ret + +lba_command: + .byte 0x10 + .byte 0x00 + .word 0x0001 + .word 0 + .word BOOTSEG +lba_sector: + .long 0, 0 + +info: .ascii "Using drive " +drive_num: + .byte 'X' + .ascii ", partition " +part_num: + .asciz "Y" + +efdmbr: .asciz "MBR on floppy of old BIOS\r\n" +eread: .asciz "\r\nRead error\r\n" +enoos: .asciz "No O/S\r\n" +enoboot: .ascii "No active partion" +crlf: .asciz "\r\n" + +endofcode: + nop + +// partion table + . = DOSPARTOFF // partion table start address +pt: .fill 0x40,1,0 + + . = 0x1fe +signature: + .short DOSMBR_SIGNATURE + . = 0x200 +