summaryrefslogtreecommitdiff
path: root/src/boot/bootloader.asm
blob: 4b803fa5458b6086ba0579091c555864e82338d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
ORG	0x7c00
BITS	16

CODE_SEG equ	gdt_code - gdt_start	; 0x8
DATA_SEG equ	gdt_data - gdt_start	; 0x10

_start:
	jmp short start
	nop

; Setup dummy BIOS Parameter block so a dumb BIOS does not corrupt us
; if it starts messing up with the parameter block
	times 33 db 0

start:
	; Set CS to 0 with label step2 as its beginning offset
	jmp 0:step2

step2:
	; Setup segments

	cli		; Disable interrupts
	mov ax, 0
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, 0x7c00	; Stack segment starts right before ds
			; and grows down to 0x0. This works because
			; Intel implements a full descending stack
	sti		; Enable interrupts

.load_protected:
	cli
	lgdt[gdt_table]
	mov eax, cr0
	or eax, 0x1
	mov cr0, eax

	jmp CODE_SEG:start_32

; GDT table description
gdt_start:
gdt_null:	; First segment Descriptor is always 0
	dd 0x0
	dd 0x0
;Second segment descriptor... Code segment - 0x8
gdt_code:
	dw 0xffff	; Segment limit 0-15
	dw 0		; Base address 0-15
	db 0		; Base 16-23
	db 0x9a		; Access byte field 0b10011010

	; We don't have a 4 bits type, so use a byte type
	; to set both Segment limit high bits and flags

	db 11001111b	; Segment limit 16-19
			; Flags 0-3
	db 0		; Base address high bits 24-31

;Second segment descriptor... Data segment - 0x10
gdt_data:
	dw 0xffff	; Segment limit 0-15
	dw 0		; Base address 0-15
	db 0		; Base 16-23
	db 0x92		; Access byte field 0b10010010
	db 11001111b	; Segment limit 16-19
			; Flags 0-3
	db 0		; Base address high bits 24-31
gdt_end:

gdt_table:
	dw gdt_end - gdt_start - 1
	dd gdt_start

[BITS 32]
start_32:
	mov eax, 1		; LBA addr to start from
	mov ecx, 100		; Num of sectors
	mov edi, 0x00100000	; Mem addr to load data read
	call ata_lba_read
	jmp CODE_SEG:0x0100000

ata_lba_read:
	; Write the high 8bits of the LBA to the disk controller
	mov ebx, eax,
	shr eax, 24	; Get LBA high 8bits to send to the controller
	or eax, 0xE0	; Select the 'master' drive
	mov dx, 0x1F6	; Port number
	out dx, al	; Write bits to the port

	; Write the number of sectors to read to the disk controller
	mov eax, ecx	; Num of sectors
	mov dx, 0x1F2	; Port number
	out dx, al	; Send to the port

	; Send LBA's first 0-7 bits
	mov eax, ebx	; Get original LBA
	mov dx, 0x1F3	; Port number
	out dx, al	; Write the low 8 bits to the controller

	; Send LBA's bits 8-15 to the controller
	mov eax, ebx	; Get original LBA
	mov dx, 0x1F4	; Port number
	shr eax, 8	; Get bits 8-15
	out dx, al	; Write 8 bits to controller

	; Send LBA's bits 16-23 to the controller
	mov eax, ebx	; Get original LBA
	mov dx, 0x1F5	; Port number
	shr eax, 16	; Get bits 16-23
	out dx, al	; Write to the controller

	; No idea what it does yet
	mov dx, 0x1F7
	mov al, 0x20
	out dx, al

	; Read sectors into memory
.next_sector:
	push ecx ; Save num of sectors

	; Loop here until the controller is ready
.try_again:
	mov dx, 0x1f7
	in al, dx
	test al, 8
	jz .try_again

	mov ecx, 256
	mov dx, 0x1F0
	rep insw	; Read 256 words from the I/O port in DX into memory ES:EDI
			; EDI has been set to 0x100000 above
			; ecx controls how many words to read (w means word in ins)
			; 256 * 2 bytes-per-word - We read a whole sector

	pop ecx			; restor num of sectors
	loop .next_sector	; This decrements ecx too

	ret

; Fill in to the end and add bootloader signature
	times 510 - ($ - $$) db 0
	dw 0xAA55