about summary refs log tree commit diff stats
path: root/apps/bos
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2020-12-19 20:59:40 -0800
committerKartik Agaram <vc@akkartik.com>2020-12-19 21:06:05 -0800
commita84fadd65d923fbb71c5c2b9884972114a0c2928 (patch)
tree1f8ae0f80656c86db8bedc28ba6c484dbf961626 /apps/bos
parentcbe8bfd87a4273993647ecdcd821e59521122c57 (diff)
downloadmu-a84fadd65d923fbb71c5c2b9884972114a0c2928.tar.gz
7355 - learning to boot into 32-bit mode
These exercises are from 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
Diffstat (limited to 'apps/bos')
-rw-r--r--apps/bos/3-1.hex51
-rw-r--r--apps/bos/3-2.hex65
-rw-r--r--apps/bos/4.asm46
-rw-r--r--apps/bos/README.md7
-rw-r--r--apps/bos/bochsrc3
-rw-r--r--apps/bos/gdt.asm46
-rw-r--r--apps/bos/print-mem-real-mode.hex67
-rw-r--r--apps/bos/print_string.asm11
-rw-r--r--apps/bos/print_string_pm.asm21
-rw-r--r--apps/bos/switch_to_pm.asm34
10 files changed, 351 insertions, 0 deletions
diff --git a/apps/bos/3-1.hex b/apps/bos/3-1.hex
new file mode 100644
index 00000000..75e10c1f
--- /dev/null
+++ b/apps/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 < apps/bos/3-1.hex > boot.bin
+# To run:
+#   qemu-system-i386 boot.bin
+# Or:
+#   bochs -f apps/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/apps/bos/3-2.hex b/apps/bos/3-2.hex
new file mode 100644
index 00000000..a52d2a88
--- /dev/null
+++ b/apps/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 < apps/bos/3-2.hex > boot.bin
+# To run:
+#   qemu-system-i386 boot.bin
+# Or:
+#   bochs -f apps/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/apps/bos/4.asm b/apps/bos/4.asm
new file mode 100644
index 00000000..ae079446
--- /dev/null
+++ b/apps/bos/4.asm
@@ -0,0 +1,46 @@
+; 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
+#
+# Expected output inside emulator:
+#   Started in 16-bit Real Mode
+#   Successfully landed in 32-bit Protected Mode
+# Look for the second line at the top of the emulated screen.
+
+; A boot sector that enters 32-bit protected mode.
+[org 0x7c00]
+
+  mov bp, 0x9000          ; Set the stack.
+  mov sp, bp
+
+  mov bx, MSG_REAL_MODE
+  call print_string
+
+  call switch_to_pm      ; Note that we never return from here.
+
+  jmp $
+
+%include "print_string.asm"
+%include "gdt.asm"
+%include "print_string_pm.asm"
+%include "switch_to_pm.asm"
+
+[bits 32]
+; This is where we arrive after switching to and initialising protected mode.
+BEGIN_PM:
+
+  mov ebx, MSG_PROT_MODE
+  call print_string_pm    ; Use our 32-bit print routine.
+
+  jmp $                      ; Hang.
+
+; 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/apps/bos/README.md b/apps/bos/README.md
new file mode 100644
index 00000000..989ebf44
--- /dev/null
+++ b/apps/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/apps/bos/bochsrc b/apps/bos/bochsrc
new file mode 100644
index 00000000..10e4d596
--- /dev/null
+++ b/apps/bos/bochsrc
@@ -0,0 +1,3 @@
+floppya: 1_44=boot.bin, status=inserted
+boot: floppy
+log: -
diff --git a/apps/bos/gdt.asm b/apps/bos/gdt.asm
new file mode 100644
index 00000000..d9703551
--- /dev/null
+++ b/apps/bos/gdt.asm
@@ -0,0 +1,46 @@
+; GDT
+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:       ; The reason for putting a label at the end of the
+               ; GDT is so we can have the assembler calculate
+               ; the size of the GDT for the GDT decriptor (below)
+
+; GDT descriptor
+gdt_descriptor:
+  dw gdt_end - gdt_start - 1  ; Size of our GDT, always less one
+                              ; of the true size
+  dd gdt_start                ; Start address of our GDT
+
+; Define some handy constants for the GDT segment descriptor offsets, which
+; are what segment registers must contain when in protected mode. For example,
+; when we set DS = 0x10 in PM , the CPU knows that we mean it to use the
+; segment described at offset 0x10 (i.e. 16 bytes) in our GDT, which in our
+; case is the DATA segment (0x0 -> NULL; 0x08 -> CODE; 0x10 -> DATA)
+CODE_SEG equ gdt_code - gdt_start
+DATA_SEG equ gdt_data - gdt_start
diff --git a/apps/bos/print-mem-real-mode.hex b/apps/bos/print-mem-real-mode.hex
new file mode 100644
index 00000000..5e41d5b3
--- /dev/null
+++ b/apps/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 < apps/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
diff --git a/apps/bos/print_string.asm b/apps/bos/print_string.asm
new file mode 100644
index 00000000..555e4d52
--- /dev/null
+++ b/apps/bos/print_string.asm
@@ -0,0 +1,11 @@
+print_string:
+    pusha
+    mov ah, 0x0e
+loop:
+    mov al, [bx]
+    int 0x10
+    add bx, 1
+    cmp al, 0
+    jne loop
+    popa
+    ret
diff --git a/apps/bos/print_string_pm.asm b/apps/bos/print_string_pm.asm
new file mode 100644
index 00000000..78904671
--- /dev/null
+++ b/apps/bos/print_string_pm.asm
@@ -0,0 +1,21 @@
+[bits  32]
+; 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
diff --git a/apps/bos/switch_to_pm.asm b/apps/bos/switch_to_pm.asm
new file mode 100644
index 00000000..aeef97c0
--- /dev/null
+++ b/apps/bos/switch_to_pm.asm
@@ -0,0 +1,34 @@
+[bits 16]
+; Switch to protected mode
+switch_to_pm:
+  cli                     ; We must switch of interrupts until we have
+                          ; set up the protected mode interrupt vector
+                          ; otherwise interrupts will run riot.
+
+  lgdt [gdt_descriptor]   ; Load our global descriptor table, which defines
+                          ; the protected mode segments (e.g. for code and data)
+
+  mov eax, cr0            ; To make the switch to protected mode, we set
+  or eax, 0x1             ; the first bit of CR0, a control register
+  mov cr0, eax
+
+  jmp CODE_SEG:init_pm    ; Make a far jump (i.e. to a new segment) to our 32-bit
+                          ; code. This also forces the CPU to flush its cache of
+                          ; prefetched and real-mode decoded instructions, which can
+                          ; cause problems.
+
+[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.
+
+  call BEGIN_PM           ; Finally, call some well-known label