From a84fadd65d923fbb71c5c2b9884972114a0c2928 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Sat, 19 Dec 2020 20:59:40 -0800 Subject: 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 --- apps/bos/3-1.hex | 51 ++++++++++++++++++++++++++++++ apps/bos/3-2.hex | 65 ++++++++++++++++++++++++++++++++++++++ apps/bos/4.asm | 46 +++++++++++++++++++++++++++ apps/bos/README.md | 7 +++++ apps/bos/bochsrc | 3 ++ apps/bos/gdt.asm | 46 +++++++++++++++++++++++++++ apps/bos/print-mem-real-mode.hex | 67 ++++++++++++++++++++++++++++++++++++++++ apps/bos/print_string.asm | 11 +++++++ apps/bos/print_string_pm.asm | 21 +++++++++++++ apps/bos/switch_to_pm.asm | 34 ++++++++++++++++++++ 10 files changed, 351 insertions(+) create mode 100644 apps/bos/3-1.hex create mode 100644 apps/bos/3-2.hex create mode 100644 apps/bos/4.asm create mode 100644 apps/bos/README.md create mode 100644 apps/bos/bochsrc create mode 100644 apps/bos/gdt.asm create mode 100644 apps/bos/print-mem-real-mode.hex create mode 100644 apps/bos/print_string.asm create mode 100644 apps/bos/print_string_pm.asm create mode 100644 apps/bos/switch_to_pm.asm 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 -- cgit 1.4.1-2-gfad0