Skip to content

Commit 0134c56

Browse files
committed
lesson 23, initial commit
1 parent d271f5b commit 0134c56

31 files changed

+1554
-0
lines changed

23-fixes/Makefile

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
C_SOURCES = $(wildcard kernel/*.c drivers/*.c cpu/*.c libc/*.c)
2+
HEADERS = $(wildcard kernel/*.h drivers/*.h cpu/*.h libc/*.h)
3+
# Nice syntax for file extension replacement
4+
OBJ = ${C_SOURCES:.c=.o cpu/interrupt.o}
5+
6+
# Change this if your cross-compiler is somewhere else
7+
CC = /usr/local/i386elfgcc/bin/i386-elf-gcc
8+
GDB = /usr/local/i386elfgcc/bin/i386-elf-gdb
9+
# -g: Use debugging symbols in gcc
10+
CFLAGS = -g -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs \
11+
-Wall -Wextra -Werror
12+
13+
# First rule is run by default
14+
os-image.bin: boot/bootsect.bin kernel.bin
15+
cat $^ > os-image.bin
16+
17+
# '--oformat binary' deletes all symbols as a collateral, so we don't need
18+
# to 'strip' them manually on this case
19+
kernel.bin: boot/kernel_entry.o ${OBJ}
20+
i386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary
21+
22+
# Used for debugging purposes
23+
kernel.elf: boot/kernel_entry.o ${OBJ}
24+
i386-elf-ld -o $@ -Ttext 0x1000 $^
25+
26+
run: os-image.bin
27+
qemu-system-i386 -fda os-image.bin
28+
29+
# Open the connection to qemu and load our kernel-object file with symbols
30+
debug: os-image.bin kernel.elf
31+
qemu-system-i386 -s -fda os-image.bin -d guest_errors,int &
32+
${GDB} -ex "target remote localhost:1234" -ex "symbol-file kernel.elf"
33+
34+
# Generic rules for wildcards
35+
# To make an object, always compile from its .c
36+
%.o: %.c ${HEADERS}
37+
${CC} ${CFLAGS} -ffreestanding -c $< -o $@
38+
39+
%.o: %.asm
40+
nasm $< -f elf -o $@
41+
42+
%.bin: %.asm
43+
nasm $< -f bin -o $@
44+
45+
clean:
46+
rm -rf *.bin *.dis *.o os-image.bin *.elf
47+
rm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o cpu/*.o libc/*.o

23-fixes/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
*Concepts you may want to Google beforehand: XX*
2+
3+
**Goal: Fix miscellaneous issues with our code**
4+
5+
The OSDev wiki has a section [which describes some issues with
6+
JamesM's tutorial](http://wiki.osdev.org/James_Molloy%27s_Tutorial_Known_Bugs).
7+
Since we followed his tutorial for lessons 18-22 (interrupts through malloc), we'll
8+
need to make sure we fix any of the issues before moving on.
9+
10+

23-fixes/boot/32bit_print.asm

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[bits 32] ; using 32-bit protected mode
2+
3+
; this is how constants are defined
4+
VIDEO_MEMORY equ 0xb8000
5+
WHITE_OB_BLACK equ 0x0f ; the color byte for each character
6+
7+
print_string_pm:
8+
pusha
9+
mov edx, VIDEO_MEMORY
10+
11+
print_string_pm_loop:
12+
mov al, [ebx] ; [ebx] is the address of our character
13+
mov ah, WHITE_OB_BLACK
14+
15+
cmp al, 0 ; check if end of string
16+
je print_string_pm_done
17+
18+
mov [edx], ax ; store character + attribute in video memory
19+
add ebx, 1 ; next char
20+
add edx, 2 ; next video memory position
21+
22+
jmp print_string_pm_loop
23+
24+
print_string_pm_done:
25+
popa
26+
ret

23-fixes/boot/bootsect.asm

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
; Identical to lesson 13's boot sector, but the %included files have new paths
2+
[org 0x7c00]
3+
KERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel
4+
5+
mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot
6+
mov bp, 0x9000
7+
mov sp, bp
8+
9+
mov bx, MSG_REAL_MODE
10+
call print
11+
call print_nl
12+
13+
call load_kernel ; read the kernel from disk
14+
call switch_to_pm ; disable interrupts, load GDT, etc. Finally jumps to 'BEGIN_PM'
15+
jmp $ ; Never executed
16+
17+
%include "boot/print.asm"
18+
%include "boot/print_hex.asm"
19+
%include "boot/disk.asm"
20+
%include "boot/gdt.asm"
21+
%include "boot/32bit_print.asm"
22+
%include "boot/switch_pm.asm"
23+
24+
[bits 16]
25+
load_kernel:
26+
mov bx, MSG_LOAD_KERNEL
27+
call print
28+
call print_nl
29+
30+
mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000
31+
mov dh, 31 ; Our future kernel will be larger, make this big
32+
mov dl, [BOOT_DRIVE]
33+
call disk_load
34+
ret
35+
36+
[bits 32]
37+
BEGIN_PM:
38+
mov ebx, MSG_PROT_MODE
39+
call print_string_pm
40+
call KERNEL_OFFSET ; Give control to the kernel
41+
jmp $ ; Stay here when the kernel returns control to us (if ever)
42+
43+
44+
BOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten
45+
MSG_REAL_MODE db "Started in 16-bit Real Mode", 0
46+
MSG_PROT_MODE db "Landed in 32-bit Protected Mode", 0
47+
MSG_LOAD_KERNEL db "Loading kernel into memory", 0
48+
MSG_RETURNED_KERNEL db "Returned from kernel. Error?", 0
49+
50+
; padding
51+
times 510 - ($-$$) db 0
52+
dw 0xaa55

23-fixes/boot/disk.asm

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
; load 'dh' sectors from drive 'dl' into ES:BX
2+
disk_load:
3+
pusha
4+
; reading from disk requires setting specific values in all registers
5+
; so we will overwrite our input parameters from 'dx'. Let's save it
6+
; to the stack for later use.
7+
push dx
8+
9+
mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read'
10+
mov al, dh ; al <- number of sectors to read (0x01 .. 0x80)
11+
mov cl, 0x02 ; cl <- sector (0x01 .. 0x11)
12+
; 0x01 is our boot sector, 0x02 is the first 'available' sector
13+
mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl')
14+
; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS
15+
; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2)
16+
mov dh, 0x00 ; dh <- head number (0x0 .. 0xF)
17+
18+
; [es:bx] <- pointer to buffer where the data will be stored
19+
; caller sets it up for us, and it is actually the standard location for int 13h
20+
int 0x13 ; BIOS interrupt
21+
jc disk_error ; if error (stored in the carry bit)
22+
23+
pop dx
24+
cmp al, dh ; BIOS also sets 'al' to the # of sectors read. Compare it.
25+
jne sectors_error
26+
popa
27+
ret
28+
29+
30+
disk_error:
31+
mov bx, DISK_ERROR
32+
call print
33+
call print_nl
34+
mov dh, ah ; ah = error code, dl = disk drive that dropped the error
35+
call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html
36+
jmp disk_loop
37+
38+
sectors_error:
39+
mov bx, SECTORS_ERROR
40+
call print
41+
42+
disk_loop:
43+
jmp $
44+
45+
DISK_ERROR: db "Disk read error", 0
46+
SECTORS_ERROR: db "Incorrect number of sectors read", 0

23-fixes/boot/gdt.asm

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps
2+
; the GDT starts with a null 8-byte
3+
dd 0x0 ; 4 byte
4+
dd 0x0 ; 4 byte
5+
6+
; GDT for code segment. base = 0x00000000, length = 0xfffff
7+
; for flags, refer to os-dev.pdf document, page 36
8+
gdt_code:
9+
dw 0xffff ; segment length, bits 0-15
10+
dw 0x0 ; segment base, bits 0-15
11+
db 0x0 ; segment base, bits 16-23
12+
db 10011010b ; flags (8 bits)
13+
db 11001111b ; flags (4 bits) + segment length, bits 16-19
14+
db 0x0 ; segment base, bits 24-31
15+
16+
; GDT for data segment. base and length identical to code segment
17+
; some flags changed, again, refer to os-dev.pdf
18+
gdt_data:
19+
dw 0xffff
20+
dw 0x0
21+
db 0x0
22+
db 10010010b
23+
db 11001111b
24+
db 0x0
25+
26+
gdt_end:
27+
28+
; GDT descriptor
29+
gdt_descriptor:
30+
dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size
31+
dd gdt_start ; address (32 bit)
32+
33+
; define some constants for later use
34+
CODE_SEG equ gdt_code - gdt_start
35+
DATA_SEG equ gdt_data - gdt_start

23-fixes/boot/kernel_entry.asm

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[bits 32]
2+
[extern main] ; Define calling point. Must have same name as kernel.c 'main' function
3+
call main ; Calls the C function. The linker will know where it is placed in memory
4+
jmp $

23-fixes/boot/print.asm

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
print:
2+
pusha
3+
4+
; keep this in mind:
5+
; while (string[i] != 0) { print string[i]; i++ }
6+
7+
; the comparison for string end (null byte)
8+
start:
9+
mov al, [bx] ; 'bx' is the base address for the string
10+
cmp al, 0
11+
je done
12+
13+
; the part where we print with the BIOS help
14+
mov ah, 0x0e
15+
int 0x10 ; 'al' already contains the char
16+
17+
; increment pointer and do next loop
18+
add bx, 1
19+
jmp start
20+
21+
done:
22+
popa
23+
ret
24+
25+
26+
27+
print_nl:
28+
pusha
29+
30+
mov ah, 0x0e
31+
mov al, 0x0a ; newline char
32+
int 0x10
33+
mov al, 0x0d ; carriage return
34+
int 0x10
35+
36+
popa
37+
ret

23-fixes/boot/print_hex.asm

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
; receiving the data in 'dx'
2+
; For the examples we'll assume that we're called with dx=0x1234
3+
print_hex:
4+
pusha
5+
6+
mov cx, 0 ; our index variable
7+
8+
; Strategy: get the last char of 'dx', then convert to ASCII
9+
; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N.
10+
; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40
11+
; Then, move the ASCII byte to the correct position on the resulting string
12+
hex_loop:
13+
cmp cx, 4 ; loop 4 times
14+
je end
15+
16+
; 1. convert last char of 'dx' to ascii
17+
mov ax, dx ; we will use 'ax' as our working register
18+
and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros
19+
add al, 0x30 ; add 0x30 to N to convert it to ASCII "N"
20+
cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F'
21+
jle step2
22+
add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7
23+
24+
step2:
25+
; 2. get the correct position of the string to place our ASCII char
26+
; bx <- base address + string length - index of char
27+
mov bx, HEX_OUT + 5 ; base + length
28+
sub bx, cx ; our index variable
29+
mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx'
30+
ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234
31+
32+
; increment index and loop
33+
add cx, 1
34+
jmp hex_loop
35+
36+
end:
37+
; prepare the parameter and call the function
38+
; remember that print receives parameters in 'bx'
39+
mov bx, HEX_OUT
40+
call print
41+
42+
popa
43+
ret
44+
45+
HEX_OUT:
46+
db '0x0000',0 ; reserve memory for our new string

23-fixes/boot/switch_pm.asm

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[bits 16]
2+
switch_to_pm:
3+
cli ; 1. disable interrupts
4+
lgdt [gdt_descriptor] ; 2. load the GDT descriptor
5+
mov eax, cr0
6+
or eax, 0x1 ; 3. set 32-bit mode bit in cr0
7+
mov cr0, eax
8+
jmp CODE_SEG:init_pm ; 4. far jump by using a different segment
9+
10+
[bits 32]
11+
init_pm: ; we are now using 32-bit instructions
12+
mov ax, DATA_SEG ; 5. update the segment registers
13+
mov ds, ax
14+
mov ss, ax
15+
mov es, ax
16+
mov fs, ax
17+
mov gs, ax
18+
19+
mov ebp, 0x90000 ; 6. update the stack right at the top of the free space
20+
mov esp, ebp
21+
22+
call BEGIN_PM ; 7. Call a well-known label with useful code

0 commit comments

Comments
 (0)