Skip to content

Commit

Permalink
x86: add small mbr example
Browse files Browse the repository at this point in the history
  • Loading branch information
johannst committed Dec 21, 2024
1 parent 888faa5 commit f8221e8
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 1 deletion.
19 changes: 19 additions & 0 deletions src/arch/x86/mbr/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
mbr: mbr.ld mbr.o
ld -o $@.elf -nostdlib -T $^
objcopy -O binary $@.elf $@

mbr.o: mbr.S
gcc -c -o $@ -m32 -ffreestanding $^

clean:
$(RM) mbr.o mbr.elf mbr

run: mbr
qemu-system-i386 -hda mbr $(QEMU_ARGS)

debug:
make run QEMU_ARGS="-s -S"

gdb:
gdb -ex 'target remote :1234' mbr.elf -ex 'layout asm' -ex 'b entry_pm32' -ex 'c'

117 changes: 117 additions & 0 deletions src/arch/x86/mbr/mbr.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
.code16
.intel_syntax noprefix

.section .boot, "ax", @progbits
// Disable interrupts.
cli

// Clear segment selectors.
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov fs, ax
mov gs, ax

// Set cs to 0x0000, as some BIOSes load the MBR to either 07c0:0000 or 0000:7c000.
jmp 0x0000:entry_rm16

entry_rm16:
// Set video mode 3, see [1].
// * 80x25 text mode
// * 640x200 pixel resolution (8x8 pixel per char)
// * 16 colors (4bit)
// * 4 pages
// * 0xB800 screen address
//
// [1] http://www.ctyme.com/intr/rb-0069.htm
mov ax, 0x3
int 0x10

// Move cursor to second row.
// http://www.ctyme.com/intr/rb-0087.htm
mov ah, 0x02
mov bh, 0 // page
mov dh, 1 // row
mov dl, 0 // col
int 0x10

// Clear direction flag for lodsb below.
cld

// Load pointer to msg_rm string (null terminated).
lea si, [msg_rm]

// Teletype output char at current cursor position.
// http://www.ctyme.com/intr/rb-0106.htm
mov ah, 0x0e
1:
lodsb // al <- ds:si ; si+=1 ; (al char to write)
test al,al // test for null terminator
jz 2f
int 0x10
jmp 1b
2:

// Enable A20 address line.
in al, 0x92
or al, 2
out 0x92, al

// Load GDT descriptor.
lgdt [gdt_desc]

// Enable protected mode (set CR0.PE bit).
mov eax, cr0
or eax, (1 << 0)
mov cr0, eax

// Far jump which loads segment selector (0x0008) into cs.
// 0x0008 -> RPL=0, TI=0(GDT), I=1
jmp 0x0008:entry_pm32

.code32
entry_pm32:
// Select data segment selector (0x0010) for ds.
mov ax, gdt_data - gdt
mov ds, ax

// Write through VGA interface (video memory).
// Each character is represented by 2 bytes.
// 4 bit bg | 4 bit fg | 8 bit ascii char
//
// Start writing at third line.
mov edi, 0xb8000 + (80 * 2 * 2) //

lea esi, [msg_pm]
1:
lodsb // al <- ds:esi ; esi+=1
test al, al // test for null terminator
jz 2f
or eax, 0x1f00 // blue bg, white fg
stosw // ds:[edi] <- ax; edi+=2
jmp 1b
2:
hlt
jmp 2b

// For simplicity keep data used by boot sector in the same section.
.balign 8
msg_rm:
.asciz "Hello from Real Mode!"
msg_pm:
.asciz "Hello from Protected Mode!"

.balign 8
gdt:
.8byte 0x0000000000000000 // 0x00 | null descriptor
.8byte 0x00cf9a000000ffff // 0x08 | 32 bit, code (rx), present, dpl=0, g=4K, base=0, limit=fffff
gdt_data:
.8byte 0x00cf92000000ffff // 0x10 | 32 bit, data (rw), present, dpl=0, g=4K, base=0, limit=fffff
gdt_desc:
.2byte .-gdt-1 // size
.4byte gdt // address

// Write MBR boot magic value.
.fill 510 - (. - .boot), 1, 0x00
.2byte 0xaa55
11 changes: 11 additions & 0 deletions src/arch/x86/mbr/mbr.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
OUTPUT_FORMAT(elf32-i386)
OUTPUT_ARCH(i386)

SECTIONS {
. = 0x7c00;
.boot : { *(.boot) }
_boot_end = .;
/DISCARD/ : { *(.*) }

ASSERT(_boot_end - 0x7c00 == 512, "boot sector must be exact 512 bytes")
}
64 changes: 63 additions & 1 deletion src/arch/x86_64.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ must must save these registers in case they are used.
- `r12` - `r15`
- `xmm6` - `xmm15`

## ASM skeleton
## ASM skeleton - linux userspace
Small assembler skeleton, ready to use with following properties:
- use raw Linux syscalls (`man 2 syscall` for ABI)
- no `C runtime (crt)`
Expand Down Expand Up @@ -350,6 +350,67 @@ To compile and run:
Hi ASM-World!
```

## MBR boot sectors example
The following shows a non-minimal [MBR][mbr] boot sector, which transitions from
16-bit _real mode_ to 32-bit _protected mode_ by setting up a small _global
descriptor table (GDT)_. A string is printed in each mode.

```x86asm
{{ #include x86/mbr/mbr.S }}
```

The linker script.
```x86asm
{{ #include x86/mbr/mbr.ld }}
```

The build instructions.
```make
{{ #include x86/mbr/Makefile:1:6 }}
```

One can boot into the bootsector from legacy BIOS, either with qemu or by
writing the mbr boot sector as first sector onto a usb stick.

```sh
qemu-system-i386 -hda mbr
```

The following gives some more detailed description for the _segment selector_
registers, the _segment descriptors_ in the GDT, and the _GDT descriptor_
itself.
```
# Segment Selector (cs, ds, es, ss, fs, gs).
[15:3] I Descriptor Index
[2:1] TI Table Indicator (0=GTD | 1=LDT)
[0] RPL Requested Privilege Level
# Segment Descriptor (2 x 4 byte words).
0x4 [31:24] Base[31:24]
0x4 [23] G Granularity, scaling of limit (0=1B | 1=4K)
0x4 [22] D/B (0=16bit | 1=32bit)
0x4 [21] L (0=compatibility mode | 1=64bit code) if 1 -> D/B = 0
0x4 [20] AVL Free use for system sw
0x4 [19:16] Limit[19:16]
0x4 [15] P Present
0x4 [14:13] DPL Descriptor privilege level
0x4 [12] S (0=system segment | 1=code/data)
0x4 [11:0] Type Code or data and access information.
0x4 [7:0] Base[23:16]
0x0 [31:16] Base[15:0]
0x0 [15:0] Limit[15:0]
# GDT descriptor (32bit mode)
[47:16] Base address of GDT table.
[15:0] Length of GDT table.
```

## References
- [SystemV AMD64 ABI][sysvabi]
- [AMD64 Vol1: Application Programming][amd64_vol1]
Expand Down Expand Up @@ -379,3 +440,4 @@ Hi ASM-World!
[gas_directives]: https://sourceware.org/binutils/docs/as/Pseudo-Ops.html#Pseudo-Ops
[gas_x86_64]: https://sourceware.org/binutils/docs/as/i386_002dDependent.html
[juicebox]: https://github.com/johannst/juicebox-asm
[mbr]: https://en.wikipedia.org/wiki/Master_boot_record

0 comments on commit f8221e8

Please sign in to comment.