diff options
Diffstat (limited to 'baremetal')
-rw-r--r-- | baremetal/README.md | 2 | ||||
-rw-r--r-- | baremetal/boot.bochsrc | 6 | ||||
-rw-r--r-- | baremetal/boot0.hex | 346 | ||||
-rw-r--r-- | baremetal/bos/3-1.hex | 51 | ||||
-rw-r--r-- | baremetal/bos/3-2.hex | 65 | ||||
-rw-r--r-- | baremetal/bos/32bit.hex | 132 | ||||
-rw-r--r-- | baremetal/bos/4.asm | 143 | ||||
-rw-r--r-- | baremetal/bos/4.xxd | 87 | ||||
-rw-r--r-- | baremetal/bos/README.md | 7 | ||||
-rw-r--r-- | baremetal/bos/bochsrc | 3 | ||||
-rw-r--r-- | baremetal/bos/print-mem-real-mode.hex | 67 |
11 files changed, 909 insertions, 0 deletions
diff --git a/baremetal/README.md b/baremetal/README.md new file mode 100644 index 00000000..af45760c --- /dev/null +++ b/baremetal/README.md @@ -0,0 +1,2 @@ +Some apps written in SubX and Mu. Where the rest of this repo relies on a few +Linux syscalls, the apps in this subdirectory interface directly with hardware. diff --git a/baremetal/boot.bochsrc b/baremetal/boot.bochsrc new file mode 100644 index 00000000..521e22d9 --- /dev/null +++ b/baremetal/boot.bochsrc @@ -0,0 +1,6 @@ +# Configuration for the Bochs x86 CPU emulator to run the output of apps/boot.hex +# See apps/boot.hex for more details. + +ata0-master: type=disk, path="disk.img", mode=flat, cylinders=20, heads=16, spt=63 # 10MB, 512 bytes per sector +boot: disk +log: - diff --git a/baremetal/boot0.hex b/baremetal/boot0.hex new file mode 100644 index 00000000..793b2b9f --- /dev/null +++ b/baremetal/boot0.hex @@ -0,0 +1,346 @@ +# A minimal bootable image that: +# - loads more sectors past the first boot sector (using BIOS primitives) +# - switches to 32-bit mode (giving up access to BIOS primitives) +# - sets up a handler for keyboard events +# - as an example program, prints '1' to the top-left position on screen (by writing to memory-mapped VGA memory) when the '1' key is typed +# +# When it's ready to accept keys, it prints 'H' to the top-left of the screen. +# +# If the initial load fails, it prints 'D' to the top-left of the screen and +# halts. +# +# To convert to a disk image, first prepare a realistically sized disk image: +# dd if=/dev/zero of=disk.img count=20160 # 512-byte sectors, so 10MB +# Now fill in sectors: +# ./bootstrap run apps/hex < baremetal/boot0.hex > boot.bin +# dd if=boot.bin of=disk.img conv=notrunc +# To run: +# qemu-system-i386 disk.img +# Or: +# bochs -f baremetal/boot.bochsrc # boot.bochsrc loads disk.img +# +# Since we start out in 16-bit mode, we need instructions SubX doesn't +# support. +# This file contains just lowercase hex bytes and comments. Zero +# error-checking. Make liberal use of: +# - comments documenting expected offsets +# - size checks on the emitted file (currently: 512 bytes) +# - xxd to eyeball that offsets contain expected bytes + +## 16-bit entry point + +# Upon reset, the IBM PC +# loads the first sector (512 bytes) +# from some bootable image (see the boot sector marker at the end of this file) +# to the address range [0x7c00, 0x7e00) + +# offset 00 (address 0x7c00): + # disable interrupts for this initialization + fa # cli + + # initialize segment registers + # this isn't always needed, but the recommendation is to not make assumptions + b8 00 00 # ax <- 0 + 8e d8 # ds <- ax + 8e d0 # ss <- ax + 8e c0 # es <- ax + 8e e0 # fs <- ax + 8e e8 # gs <- ax + + # We don't read or write the stack before we get to 32-bit mode. No function + # calls, so we don't need to initialize the stack. + +# 0e: + # load more sectors from disk + b4 02 # ah <- 2 # read sectors from disk + # dl comes conveniently initialized at boot time with the index of the device being booted + b5 00 # ch <- 0 # cylinder 0 + b6 00 # dh <- 0 # track 0 + b1 02 # cl <- 2 # second sector, 1-based + b0 01 # al <- 1 # number of sectors to read + # address to write sectors to = es:bx = 0x7e00, contiguous with boot segment + bb 00 00 # bx <- 0 + 8e c3 # es <- bx + bb 00 7e # bx <- 0x7e00 + cd 13 # int 13h, BIOS disk service + 0f 82 76 00 # jump-if-carry disk-error + +# 26: + # undo the A20 hack: https://en.wikipedia.org/wiki/A20_line + # this is from https://github.com/mit-pdos/xv6-public/blob/master/bootasm.S + # seta20.1: + e4 64 # al <- port 0x64 + a8 02 # set zf if bit 1 (second-least significant) is not set + 75 fa # if zf not set, goto seta20.1 (-6) + + b0 d1 # al <- 0xd1 + e6 64 # port 0x64 <- al + +# 30: + # seta20.2: + e4 64 # al <- port 0x64 + a8 02 # set zf if bit 1 (second-least significant) is not set + 75 fa # if zf not set, goto seta20.2 (-6) + + b0 df # al <- 0xdf + e6 64 # port 0x64 <- al + +# 3a: + # switch to 32-bit mode + 0f 01 16 # lgdt 00/mod/indirect 010/subop 110/rm/use-disp16 + 80 7c # *gdt_descriptor +# 3f: + 0f 20 c0 # eax <- cr0 + 66 83 c8 01 # eax <- or 0x1 + 0f 22 c0 # cr0 <- eax + ea c0 7c 08 00 # far jump to initialize_32bit_mode after setting cs to the record at offset 8 in the gdt (gdt_code) + +# padding +# 4e: + 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +## GDT: 3 records of 8 bytes each + +# 60: +# gdt_start: +# gdt_null: mandatory null descriptor + 00 00 00 00 00 00 00 00 +# gdt_code: (offset 8 from gdt_start) + ff ff # limit[0:16] + 00 00 00 # base[0:24] + 9a # 1/present 00/privilege 1/descriptor type = 1001b + # 1/code 0/conforming 1/readable 0/accessed = 1010b + cf # 1/granularity 1/32-bit 0/64-bit-segment 0/AVL = 1100b + # limit[16:20] = 1111b + 00 # base[24:32] +# gdt_data: (offset 16 from gdt_start) + ff ff # limit[0:16] + 00 00 00 # base[0:24] + 92 # 1/present 00/privilege 1/descriptor type = 1001b + # 0/data 0/conforming 1/readable 0/accessed = 0010b + cf # same as gdt_code + 00 # base[24:32] +# gdt_end: + +# padding +# 78: + 00 00 00 00 00 00 00 00 + +# 80: +# gdt_descriptor: + 17 00 # final index of gdt = gdt_end - gdt_start - 1 + 60 7c 00 00 # start = gdt_start + +# padding +# 85: + 00 00 00 00 00 00 00 00 00 00 + +# 90: +# disk_error: + # print 'D' to top-left of screen to indicate disk error + # *0xb8000 <- 0x0f44 + # bx <- 0xb800 + bb 00 b8 + # ds <- bx + 8e db # 11b/mod 011b/reg/ds 011b/rm/bx + # al <- 'D' + b0 44 + # ah <- 0x0f # white on black + b4 0f + # bx <- 0 + bb 00 00 + # *ds:bx <- ax + 89 07 # 00b/mod/indirect 000b/reg/ax 111b/rm/bx + +e9 fb ff # loop forever + +# padding +# a1: + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +## 32-bit code from this point (still some instructions not in SubX) + +# c0: +# initialize_32bit_mode: + 66 b8 10 00 # ax <- offset 16 from gdt_start + 8e d8 # ds <- ax + 8e d0 # ss <- ax + 8e c0 # es <- ax + 8e e0 # fs <- ax + 8e e8 # gs <- ax + + # load interrupt handlers + 0f 01 1d # lidt 00/mod/indirect 011/subop 101/rm32/use-disp32 + 00 7f 00 00 # *idt_descriptor + + # enable keyboard IRQ + b0 fd # al <- 0xfd # enable just IRQ1 + e6 21 # port 0x21 <- al + + # initialization is done; enable interrupts + fb + e9 21 00 00 00 # jump to 0x7d00 + +# padding +# df: + 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +## 'application' SubX code: print one character to top-left of screen + +# offset 100 (address 0x7d00): +# Entry: + # eax <- *0x7ff4 # random address in second segment containing 'H' + 8b # copy rm32 to r32 + 05 # 00/mod/indirect 000/r32/eax 101/rm32/use-disp32 + # disp32 + f4 7f 00 00 + # *0xb8000 <- eax + 89 # copy r32 to rm32 + 05 # 00/mod/indirect 000/r32/eax 101/rm32/use-disp32 + # disp32 + 00 80 0b 00 + +e9 fb ff ff ff # loop forever + +# padding +# 111: + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +# 120: +# null interrupt handler: + cf # iret + +# padding +# 121: + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +# 130: +# keyboard interrupt handler: + # prologue + fa # disable interrupts + 60 # push all registers to stack + # acknowledge interrupt + b0 20 # al <- 0x20 + e6 20 # port 0x20 <- al + # read keyboard status (TODO: why bit 0? Doesn't line up with https://web.archive.org/web/20040604041507/http://panda.cs.ndsu.nodak.edu/~achapwes/PICmicro/keyboard/atkeyboard.html) +#? e4 64 # al <- port 0x64 +#? a8 01 # set zf if bit 0 (least significant) is not set +#? 74 11 # if bit 0 is not set, skip to epilogue + # read keycode into eax + 31 c0 # eax <- xor eax; 11/direct 000/r32/eax 000/rm32/eax + e4 60 # al <- port 0x60 + # map key '1' to ascii; if eax == 2, eax = 0x31 + 3d 02 00 00 00 # compare eax with 0x02 + 75 0b # if not equal, goto epilogue + b8 31 0f 00 00 # eax <- 0x0f31 + # print eax to top-left of screen (*0xb8000) + 89 # copy r32 to rm32 + 05 # 00/mod/indirect 000/r32/eax 101/rm32/use-disp32 + # disp32 + 00 80 0b 00 + # epilogue + 61 # pop all registers + fb # enable interrupts + cf # iret + +# padding +# 14f + 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +# final 2 bytes of boot sector +55 aa + +## sector 2 +# loaded by load_disk, not automatically on boot + +# offset 200 (address 0x7e00): interrupt descriptor table +# 32 entries * 8 bytes each = 256 bytes (0x100) +# idt_start: + +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 + +# entry 8: clock + 20 7d # target[0:16] = null interrupt handler + 08 00 # segment selector (gdt_code) + 00 # unused + 8e # 1/p 00/dpl 0 1110/type/32-bit-interrupt-gate + 00 00 # target[16:32] + +# entry 9: keyboard + 30 7d # target[0:16] = keyboard interrupt handler + 08 00 # segment selector (gdt_code) + 00 # unused + 8e # 1/p 00/dpl 0 1110/type/32-bit-interrupt-gate + 00 00 # target[16:32] + +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +# idt_end: + +# offset 300 (address 0x7f00): +# idt_descriptor: + ff 00 # idt_end - idt_start - 1 + 00 7e 00 00 # start = idt_start + +# padding + 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 48 0f 00 00 00 00 00 00 00 00 00 00 # spot the 'H' with attributes +# offset 400 (address 0x8000) + +# vim:ft=conf diff --git a/baremetal/bos/3-1.hex b/baremetal/bos/3-1.hex new file mode 100644 index 00000000..bf4ef208 --- /dev/null +++ b/baremetal/bos/3-1.hex @@ -0,0 +1,51 @@ +# Bootable image. +# Boot sector must have exactly 512 bytes. +# 16-bit real mode. +# +# To convert to a disk image: +# ./bootstrap run apps/hex < baremetal/bos/3-1.hex > boot.bin +# To run: +# qemu-system-i386 boot.bin +# Or: +# bochs -f baremetal/bos/bochsrc # bochsrc loads boot.bin +# +# Expected output inside emulator: +# Booting from floppy... + +e9 fd ff + +# padding to 512 bytes + 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa + +# vim:ft=subx diff --git a/baremetal/bos/3-2.hex b/baremetal/bos/3-2.hex new file mode 100644 index 00000000..85792e26 --- /dev/null +++ b/baremetal/bos/3-2.hex @@ -0,0 +1,65 @@ +# Bootable image. +# Boot sector must have exactly 512 bytes. +# 16-bit real mode. +# +# To convert to a disk image: +# ./bootstrap run apps/hex < baremetal/bos/3-2.hex > boot.bin +# To run: +# qemu-system-i386 boot.bin +# Or: +# bochs -f baremetal/bos/bochsrc # bochsrc loads boot.bin +# +# Expected output inside emulator: +# Hello + +b4 0e # ah <- 0e (teletype output) + +b0 48 # al <- 'H' +cd 10 # int 10h +b0 65 # al <- 'e' +cd 10 # int 10h +b0 6c # al <- 'l' +cd 10 # int 10h +b0 6c # al <- 'l' +cd 10 # int 10h +b0 6f # al <- 'o' +cd 10 # int 10h + +e9 fd ff # loop forever + +# padding to 512 bytes + 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +55 aa # final 2 bytes: boot sector marker + +# vim:ft=subx diff --git a/baremetal/bos/32bit.hex b/baremetal/bos/32bit.hex new file mode 100644 index 00000000..5a85aa36 --- /dev/null +++ b/baremetal/bos/32bit.hex @@ -0,0 +1,132 @@ +# Bootable image demonstrating printing to screen in 32-bit mode. +# Must have exactly 512 bytes. +# +# To convert to a disk image: +# ./bootstrap run apps/hex < baremetal/bos/32bit.hex > boot.bin +# To run: +# qemu-system-i386 boot.bin +# Or: +# bochs -f baremetal/bos/bochsrc # bochsrc loads boot.bin +# +# Expected output inside emulator: +# H + +## 16-bit entry point + +# Boot image starts executing at address 0x7c00, +# and so occupies [0x7c00, 0x7e00). +# We don't read or write the stack before we get to 32-bit mode. + +# 00: + fa # cli # TODO: don't forget to reenable interrupts at some point + 0f 01 16 # lgdt 00/mod/indirect 010/subop 110/rm32/TODO + 80 7c # *gdt_descriptor + 0f 20 c0 # eax <- cr0 + 66 83 c8 01 # eax <- or 0x1 + 0f 22 c0 # cr0 <- eax + ea c0 7c 08 00 # far jump to initialize_32bit_mode after setting cs to the record at offset 8 in the gdt (gdt_code) + +# padding +# 15: + 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +## GDT: 3 records of 8 bytes each + +# 60: +# gdt_start: +# gdt_null: mandatory null descriptor + 00 00 00 00 00 00 00 00 +# gdt_code: (offset 8 from gdt_start) + ff ff # limit[0:16] + 00 00 00 # base[0:24] + 9a # 1/present 00/privilege 1/descriptor type = 1001b + # 1/code 0/conforming 1/readable 0/accessed = 1010b + cf # 1/granularity 1/32-bit 0/64-bit-segment 0/AVL = 1100b + # limit[16:20] = 1111b + 00 # base[24:32] +# gdt_data: (offset 16 from gdt_start) + ff ff # limit[0:16] + 00 00 00 # base[0:24] + 92 # 1/present 00/privilege 1/descriptor type = 1001b + # 0/data 0/conforming 1/readable 0/accessed = 0010b + cf # same as gdt_code + 00 # base[24:32] +# gdt_end: + +# padding +# 78: + 00 00 00 00 00 00 00 00 + +# 80: +# gdt_descriptor: + 17 00 # final index of gdt = gdt_end - gdt_start - 1 + 60 7c 00 00 # start = gdt_start + +# padding +# 85: + 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +## 32-bit code from this point (still some instructions not in SubX) + +# c0: +# initialize_32bit_mode: + 66 b8 10 00 # ax <- offset 16 from gdt_start + 8e d8 # ds <- ax + 8e d0 # ss <- ax + 8e c0 # es <- ax + 8e e0 # fs <- ax + 8e e8 # gs <- ax + e9 1d 00 00 00 # jump to 0x7cf0 + +# padding +# d3: + 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +## 'application' SubX code: print one character to top-left of screen + +# f0: +# Entry: + # *0xb8000 <- 0x0f48 + c7 # opcode + # modrm + 05 # 00/mod/indirect 000/subop/copy 101/rm32/use-disp32 + # disp32 + 00 80 0b 00 + # imm32 + 48 # 'H' + 0f # white on black + 00 00 + +e9 fb ff ff ff # loop forever + +# padding to 512 bytes + 00 +# 100: +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +55 aa # final 2 bytes: boot sector marker + +# vim:ft=subx diff --git a/baremetal/bos/4.asm b/baremetal/bos/4.asm new file mode 100644 index 00000000..71c51d97 --- /dev/null +++ b/baremetal/bos/4.asm @@ -0,0 +1,143 @@ +; A boot sector that enters 32-bit protected mode. +; +; To convert to a disk image: +; cd apps/bos +; nasm 4.asm -f bin -o boot.bin +; To run: +; qemu-system-i386 boot.bin +; Or: +; bochs # bochsrc loads boot.bin + +[org 0x7c00] + + mov bp, 0x9000 + mov sp, bp + + mov bx, MSG_REAL_MODE + call print_string + + call switch_to_pm + ; never gets here + + jmp $ + +switch_to_pm: + cli + lgdt [gdt_descriptor] + ; set LSB of CR0 + mov eax, cr0 + or eax, 0x1 + mov cr0, eax + jmp CODE_SEG:init_pm ; Far jump to a new segment containing 32-bit code. + ; This also forces the CPU to flush its cache of + ; prefetched and real-mode decoded instructions. + ; never gets here + +;; === GDT stuff +gdt_start: + +gdt_null: ; the mandatory null descriptor + dd 0x0 ; ’dd’ means define double word (i.e. 4 bytes) + dd 0x0 + +gdt_code: ; the code segment descriptor + ; base=0x0, limit=0xfffff, + ; 1st flags: (present)1 (privilege)00 (descriptor type)1 -> 1001b + ; type flags: (code)1 (conforming)0 (readable)1 (accessed )0 -> 1010b + ; 2nd flags: (granularity)1 (32-bit default)1 (64-bit seg)0 (AVL)0 -> 1100b + dw 0xffff ; Limit (bits 0-15) + dw 0x0 ; Base (bits 0-15) + db 0x0 ; Base (bits 16 -23) + db 10011010b ; 1st flags, type flags + db 11001111b ; 2nd flags, Limit (bits 16-19) + db 0x0 ; Base (bits 24 -31) + +gdt_data: ; the data segment descriptor + ; Same as code segment except for the type flags: + ; type flags: (code)0 (expand down)0 (writable)1 (accessed)0 -> 0010b + dw 0xffff ; Limit (bits 0-15) + dw 0x0 ; Base (bits 0-15) + db 0x0 ; Base (bits 16 -23) + db 10010010b ; 1st flags, type flags + db 11001111b ; 2nd flags, Limit (bits 16-19) + db 0x0 ; Base (bits 24 -31) + +gdt_end: + +gdt_descriptor: + dw gdt_end - gdt_start - 1 ; last valid byte + dd gdt_start + +; some handy constants +CODE_SEG equ gdt_code - gdt_start +DATA_SEG equ gdt_data - gdt_start +;; === end GDT stuff + +;; some 16-bit helpers + +; print string whose address is in bx +print_string: + pusha + mov ah, 0x0e +loop: + mov al, [bx] + int 0x10 + add bx, 1 + cmp al, 0 + jne loop + popa + ret + +;; 32-bit code starts here +[bits 32] + +; Initialise registers and the stack once in PM. +init_pm: + + mov ax, DATA_SEG ; Now in PM, our old segments are meaningless, + mov ds, ax ; so we point our segment registers to the + mov ss, ax ; data selector we defined in our GDT + mov es, ax + mov fs, ax + mov gs, ax + + mov ebp, 0x90000 ; Update our stack position so it is right + mov esp, ebp ; at the top of the free space. + + ;; Protected Mode is now initialized + + mov ebx, MSG_PROT_MODE + call print_string_pm + + ; all done; hang + jmp $ + +; Define some constants +VIDEO_MEMORY equ 0xb8000 +WHITE_ON_BLACK equ 0x0f + +; prints a null -terminated string pointed to by EDX +print_string_pm: + pusha + mov edx, VIDEO_MEMORY ; Set edx to the start of vid mem. +print_string_pm_loop: + mov al, [ebx] ; Store the char at EBX in AL + mov ah, WHITE_ON_BLACK ; Store the attributes in AH + cmp al, 0 ; if (al == 0), at end of string , so + je print_string_pm_done + mov [edx], ax ; Store char and attributes at current + ; character cell. + add ebx , 1 ; Increment EBX to the next char in string. + add edx , 2 ; Move to next character cell in vid mem. + jmp print_string_pm_loop ; loop around to print the next char. +print_string_pm_done: + popa + ret ; Return from the function + +; Global variables +MSG_REAL_MODE db "Started in 16-bit Real Mode", 0 +MSG_PROT_MODE db "Successfully landed in 32-bit Protected Mode", 0 + +; Bootsector padding +times 510-($-$$) db 0 +dw 0xaa55 diff --git a/baremetal/bos/4.xxd b/baremetal/bos/4.xxd new file mode 100644 index 00000000..be677134 --- /dev/null +++ b/baremetal/bos/4.xxd @@ -0,0 +1,87 @@ +## 16-bit code + +# Entry: +00: + bd 00 90 # bp <- 0x9000 + 89 ec # sp <- bp + + bb 8f 7c # bx <- MSG_REAL_MODE + e8 38 00 # call print_string + + e8 02 00 # call switch_to_pm + eb fe # jump $ (should never get here) + +# switch_to_pm: +10: + fa # cli + 0f 01 16 3d 7c # lgdt [gdt_descriptor] + 0f 20 c0 # eax <- cr0 + 66 83 c8 01 # eax <- or 0x1 + 0f 22 c0 # cr0 <- eax +20: + ea 53 7c 08 00 # jmp CODE_SEG:init_pm + +# gdt_start: +# gdt_null: mandatory null descriptor + 00 00 00 00 00 00 00 00 +# gdt_code: + ff ff 00 +30: + 00 00 9a cf 00 +# gdt_data: + ff ff 00 00 00 92 cf 00 +# gdt_end: + +# gdt_descriptor: +3d: + 17 00 # limit + 25 7c 00 00 # start + +# print_string: +43: + 60 # pusha + b4 0e # ah <- 0x0e +# loop: + 8a 07 # al <- *bx + cd 10 # int 10h + 83 c3 01 # add bx, 1 + 3c 00 # cmp al, 0 + 75 f5 # jne loop + 61 # popa + c3 # ret + +## 32-bit code + +# init_pm: +53: + 66 b8 10 00 # ax <- DATA_SEG + 8e d8 # ds <- ax + 8e d0 # ss <- ax + 8e c0 # es <- ax + 8e e0 # fs <- ax + 8e e8 # gs <- ax +61: + bd 00 00 09 00 # ebp <- 0x90000 + 89 ec # esp <- ebp + bb ab 7c 00 00 # ebx <- MSG_PROT_MODE + e8 02 00 00 00 # call print_string_pm + eb fe # hang + +# print_string_pm: +74: + 60 # pusha + ba 00 80 0b 00 8a 03 b4 0f 3c 00 +80: + 74 0b 66 89 02 83 c3 01 83 c2 02 eb ed 61 c3 53 +90: + 74 61 72 74 65 64 20 69 6e 20 31 36 2d 62 69 74 +a0: + 20 52 65 61 6c 20 4d 6f 64 65 00 53 75 63 63 65 +b0: + 73 73 66 75 6c 6c 79 20 6c 61 6e 64 65 64 20 69 +c0: + 6e 20 33 32 2d 62 69 74 20 50 72 6f 74 65 63 74 +d0: + 65 64 20 4d 6f 64 65 + +# vim:ft=conf diff --git a/baremetal/bos/README.md b/baremetal/bos/README.md new file mode 100644 index 00000000..989ebf44 --- /dev/null +++ b/baremetal/bos/README.md @@ -0,0 +1,7 @@ +Exercises while reading the incomplete "Writing a simple operating system from +scratch" by Nick Blundell. + https://www.cs.bham.ac.uk/~exr/lectures/opsys/10\_11/lectures/os-dev.pdf + +The name of this directory is a reference to "Bootstrapping a simple compiler +from nothing" by Edmund Grimley-Evans. + https://github.com/certik/bcompiler diff --git a/baremetal/bos/bochsrc b/baremetal/bos/bochsrc new file mode 100644 index 00000000..de225b4c --- /dev/null +++ b/baremetal/bos/bochsrc @@ -0,0 +1,3 @@ +ata0-master: type=disk, path="boot.bin", mode=flat, cylinders=1, heads=1, spt=1 +boot: disk +log: - diff --git a/baremetal/bos/print-mem-real-mode.hex b/baremetal/bos/print-mem-real-mode.hex new file mode 100644 index 00000000..3610308c --- /dev/null +++ b/baremetal/bos/print-mem-real-mode.hex @@ -0,0 +1,67 @@ +# Experiment: write to video RAM from 16-bit real mode. And it works. +# +# To convert to a disk image: +# ./bootstrap run apps/hex < baremetal/bos/print-mem-real-mode.hex > boot.bin +# To run: +# qemu-system-i386 boot.bin +# Or: +# bochs # reads bochsrc, which loads boot.bin + +# - figure 4.1 of https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf in protected mode +# edx <- 0xb8000 +# al <- 'H' +# ah <- 0x0f # white on black +# *edx <- ax + +# - translating to real mode +# bx <- 0xb800 +bb 00 b8 +# ds <- bx +8e db # 11b/mod 011b/reg/ds 011b/rm/bx +# al <- 'H' +b0 48 +# ah <- 0x0f # white on black +b4 0f +# bx <- 0 +bb 00 00 +# *ds:bx <- ax +89 07 # 00b/mod/indirect 000b/reg/ax 111b/rm/bx + +e9 fd ff # loop forever + +# padding to 512 bytes + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +55 aa # final 2 bytes: boot sector marker + +# vim:ft=subx |