first commit

This commit is contained in:
Hojun-Cho 2024-11-27 17:06:47 +09:00
commit 3f556cba19
23 changed files with 1498 additions and 0 deletions

52
.gitignore vendored Normal file
View File

@ -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

15
Makefile Normal file
View File

@ -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

12
Makefile.inc Normal file
View File

@ -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

2
README.md Normal file
View File

@ -0,0 +1,2 @@
### MBR bootloader

18
biosboot/Makefile Normal file
View File

@ -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)

203
biosboot/biosboot.S Normal file
View File

@ -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

17
biosboot/ld.script Normal file
View File

@ -0,0 +1,17 @@
PHDRS
{
text PT_LOAD;
}
SECTIONS
{
.text :
{
*(.text)
} :text
/DISCARD/ :
{
*(.note.gnu.property)
*(.comment)
}
}

17
boot/Makefile Normal file
View File

@ -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)

56
boot/boot.c Normal file
View File

@ -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)
;
}

74
boot/gdt.S Normal file
View File

@ -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

26
boot/ld.script Normal file
View File

@ -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)
}
}

47
boot/srt0.S Normal file
View File

@ -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;

96
dat.h Normal file
View File

@ -0,0 +1,96 @@
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <limits.h>
#include <assert.h>
#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;
}

22
def.h Normal file
View File

@ -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") */

18
install.sh Executable file
View File

@ -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

15
installboot/Makefile Normal file
View File

@ -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

65
installboot/elf32.h Normal file
View File

@ -0,0 +1,65 @@
#include <elf.h>
/* 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;
}

34
installboot/filecopy.sh Executable file
View File

@ -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

244
installboot/installboot.c Normal file
View File

@ -0,0 +1,244 @@
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/mount.h>
#include <sys/sysmacros.h>
#include <sys/wait.h>
#include <errno.h>
#include <assert.h>
#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;
}

15
installmbr/Makefile Normal file
View File

@ -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)

238
installmbr/installmbr.c Normal file
View File

@ -0,0 +1,238 @@
#include <fcntl.h>
#include <unistd.h>
#include <linux/fs.h>
#include <stdio.h>
#include <limits.h>
#include <endian.h>
#include <stdlib.h>
#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;
}

19
mbr/Makefile Normal file
View File

@ -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)

193
mbr/mbr.S Normal file
View File

@ -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