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
|