os

Toy OS
git clone git://git.margiolis.net/os.git
Log | Files | Refs | README | LICENSE

boot.asm (6022B)


      1 ; Load code to 0x7c00 (Boot Sector address).
      2 [org 0x7c00]
      3 [bits 16]
      4 
      5 MAGICOFF	equ (0x7c00 + 510)
      6 KERNOFF		equ 0x1000
      7 
      8 section .text
      9 	global _start
     10 
     11 ; Entry point.
     12 _start:
     13 	cli			; Disable interrupts.
     14 	xor	ax, ax		; Clear segment registers.
     15 	mov	ds, ax
     16 	mov	es, ax
     17 	mov	fs, ax
     18 	mov	gs, ax
     19 	mov	ss, ax
     20 	mov	bp, 0x9000	; Set the base and stack pointers.
     21 	mov	sp, bp
     22 	cld			; Read strings from low to high.
     23 	sti			; Enable interrupts back.
     24 
     25 	call	a20_test
     26 	call	a20_enable
     27 	call	kernel_load	; Read kernel from disk to memory.
     28 	jmp	pm_enter	; Enter 32-bit Protected Mode.
     29 	jmp	$		; Safety hang.
     30 
     31 ; Test to see if the A20 line is enabled.
     32 ; As a reference we will use the magic number 0xaa55 since we know it's always
     33 ; going to have the same value and be located at address 0x7dfe (0x7c00 + 510).
     34 ; If the return code in `ax` is 1, the A20 is enabled, and if it's 0 it's disabled.
     35 a20_test:
     36 	pusha
     37 	
     38 	mov	ax, [MAGICOFF] ; The magic number is there.
     39 	mov	dx, ax
     40 
     41 ; We'll try to advance 1MB in memory. If the end result hasn't wrapped up
     42 ; around zero, it means that our processor is able to access more than 1MB
     43 ; of addresses, so the A20 is enabled. Using 0x7dfe again, the expected
     44 ; address after the 1MB advance should be:
     45 ;
     46 ;	0x100000 + 0x7dfe = 0x107dfe
     47 ;
     48 ; The formula for memory segmentation is:
     49 ; 
     50 ;	address = segment * 16 + offset
     51 ;
     52 ; To find the offset, the segment will be 0xffff, so `0xffff * 16 = 0xffff0`.
     53 ; Applying the formula to calculate our offset, we have:
     54 ; 
     55 ;	0xffff0 + offset = 0x107dfe =>
     56 ;	offset = 0x107dfe - 0xffff0 =>
     57 ;	offset = 0x7e0e
     58 
     59 	push	bx
     60 	mov	bx, 0xffff
     61 	mov	es, bx		; Assign value to the segment register.
     62 	pop	bx
     63 	
     64 	mov	bx, 0x7e0e
     65 	mov	dx, [es:bx]	; If the A20 line is disabled we get 0xaa55.
     66 
     67 	cmp	ax, dx
     68 	je	cont		; If they're equal, the A20 line might be disabled.
     69 	popa
     70 	mov	ax, 0x01	; Success code 1.
     71 	ret
     72 cont:
     73 	mov	ax, [0x7dff]
     74 	mov	dx, ax
     75 
     76 	push	bx
     77 	mov	bx, 0xffff	; Make another segment.
     78 	mov	es, bx
     79 	pop	bx
     80 
     81 	mov	bx, 0x7e0f
     82 	mov	dx, [es:bx]
     83 
     84 	cmp	ax, dx		; Now it really is disabled if ax == [es:bx].
     85 	je	exit
     86 	popa
     87 	mov	ax, 0x01	; Success code 1.
     88 	ret
     89 exit:
     90 	popa
     91 	xor	ax, ax		; Error code 0.	
     92 	ret
     93 
     94 ; Enable the A20 line. We'll try enabling it using the following ways:
     95 ;	- BIOS interrupt.
     96 ;	- Keyboard controller.
     97 ;	- FastA20.
     98 a20_enable:
     99 	pusha
    100 
    101 	; BIOS interrupt.
    102 	mov	ax, 0x2401	; A20-Gate Active.
    103 	int	0x15
    104 
    105 	call	a20_test
    106 	cmp	ax, 0x01
    107 	je	a20_done
    108 	jmp	a20_fail
    109 
    110 	; Keyboard controller.
    111 	sti			; Enable interrupts.
    112 
    113 	call	a20_waitc
    114 	mov	al, 0xad	; Disable controller.
    115 	out	0x64, al	; Send data to port 0x64.
    116 	call	a20_waitc
    117 	mov	al, 0xd0	; We want to read from the controller.
    118 	out	0x64, al
    119 	
    120 	call	a20_waitd
    121 	in	al, 0x60	; Read data from port 0x60.
    122 	push	ax		; Save data.
    123 
    124 	call	a20_waitc
    125 	mov	al, 0xd1	; We want to send data.
    126 	out	0x64, al
    127 	
    128 	call	a20_waitc
    129 	pop	ax
    130 	or	al, 0x02	; Write the second bit back.
    131 	out	0x60, al	; Send data to the data port.
    132 
    133 	call	a20_waitc
    134 	mov	al, 0xae	; Enable controller.
    135 	out	0x64, al
    136 	
    137 	call	a20_waitc
    138 	sti			; Enable interrupts again.
    139 
    140 	call	a20_test
    141 	cmp	ax, 0x01
    142 	je	a20_done
    143 	jmp	a20_fail
    144 
    145 ; Wait for the keyboard controller to be available for receiving commands.
    146 a20_waitc:
    147 	in	al, 0x64	; Read from port 0x64.
    148 	test	al, 0x02	; Test the second bit.
    149 	jnz	a20_waitc		; If it's 1, it's busy.
    150 	ret
    151 ; Wait for the keyboard controller to send data back.
    152 a20_waitd:
    153 	in	al, 0x64	; Read from port 0x64 again.
    154 	test	al, 0x01	; Test the first bit.
    155 	jz	a20_waitd		; If it's 1, the data is not ready to be sent.
    156 	ret
    157 
    158 ; FastA20
    159 	in	al, 0x92	; FastA20 uses port 0x92. 
    160 	or	al, 0x02	; Mask second bit.
    161 	out	0x92, al	; Send data back to port 0x92.
    162 
    163 	call	a20_test
    164 	cmp	ax, 0x01
    165 	je	a20_done
    166 	jmp	a20_fail
    167 
    168 ; The A20 is enabled. Go on.
    169 a20_done:
    170 	popa
    171 	ret
    172 
    173 ; The A20 is disabled.
    174 a20_fail:
    175 	mov	si, str_a20_fail
    176 	call	puts
    177 	jmp	$	
    178 
    179 ; Load kernel from disk to memory.
    180 kernel_load:
    181 	pusha
    182 	mov	ah, 0x42	; BIOS read disk extended function.
    183 	mov	dl, [BOOTDRV]	; Set boot drive.
    184 	mov	si, disk_packet
    185 	int	0x13		; BIOS read disk interrupt.
    186 
    187 	jc	diskerr		; There's an error. Too bad.
    188 	popa
    189 	ret
    190 diskerr:
    191 	mov	si, str_diskerr
    192 	call	puts
    193 	jmp	$		; There's nothing we can do at this point.
    194 
    195 disk_packet:
    196 	.size	db 0x10
    197 	.zero	db 0x00
    198 	.count	dw 0x0f
    199 	.off16	dw KERNOFF
    200 	.seg16	dw 0x0000
    201 	.lba	dq 1
    202 
    203 ; Set up the GDT (Global Descriptor Table).
    204 gdt:
    205 gdt_null:
    206 	dq	0x00000000
    207 
    208 gdt_kernel_code:
    209 	dw	0xffff
    210 	dw	0x0000
    211 	db	0x00
    212 	db	10011010b
    213 	db	11001111b
    214 	db	0x00
    215 
    216 gdt_kernel_data:
    217 	dw	0xffff
    218 	dw	0x0000
    219 	db	0x00
    220 	db	10010010b
    221 	db	11001111b
    222 	db	0x00
    223 
    224 gdt_userland_code:
    225 	dw	0xffff
    226 	dw	0x0000
    227 	db	0x00
    228 	db	11111010b
    229 	db	11001111b
    230 	db	0x00
    231 
    232 gdt_userland_data:
    233 	dw	0xffff
    234 	dw	0x0000
    235 	db	0x00
    236 	db	11110010b
    237 	db	11001111b
    238 	db	0x00
    239 
    240 gdt_ptr:
    241 	dw	$ - gdt - 1
    242 	dd	gdt
    243 
    244 GDT_CODESEG	equ gdt_kernel_code - gdt
    245 GDT_DATASEG	equ gdt_kernel_data - gdt
    246 
    247 ; Set up the GDT and go into Protected Mode.
    248 pm_enter:
    249 	cli			; Disable BIOS interrupts.
    250 	lgdt	[gdt_ptr]	; Load the GDT.
    251 	mov	eax, cr0
    252 	or	eax, 1		; Protected Mode Enable.
    253 	mov	cr0, eax	; There's no going back now.
    254 	jmp	GDT_CODESEG:pm_init
    255 
    256 ; We're in Protected Mode.
    257 [bits 32]
    258 pm_init:
    259 	mov	ax, GDT_DATASEG
    260 	mov	ds, ax
    261 	mov	es, ax
    262 	mov	fs, ax
    263 	mov	gs, ax
    264 	mov	ss, ax
    265 
    266 	mov	ebp, 0x90000
    267 	mov	esp, ebp
    268 
    269 	call	kernel_exec
    270 	jmp	$
    271 
    272 ; Hand control over to the C kernel. Godspeed! You Black Emperor.
    273 kernel_exec:
    274 	call	KERNOFF
    275 	jmp	$
    276 
    277 ; Print a null-terminated string.
    278 [bits 16]
    279 puts:
    280 	pusha
    281 	mov	ah, 0x0e	; Tell BIOS to display a character.
    282 loop:
    283 	mov	al, [si]	; Load character.
    284 	cmp	al, 0		; Check for \0.
    285 	jne	putchar		; If it's not \0, print the character.
    286 	popa			; We're done, pop everything.
    287 	ret			; Return back to where we were.
    288 putchar:
    289 	int	0x10		; BIOS print interrupt.
    290 	inc	si		; `str++`
    291 	jmp	loop		; Go to the next character.
    292 
    293 ; String declarations.
    294 str_diskerr:	db "Error loading disk.", 0x0a, 0x0d, 0x00
    295 str_a20_fail:	db "The A20 Line is disabled", 0x0a, 0x0d, 0x00
    296 
    297 BOOTDRV:	db 0x80
    298 
    299 ; Padding to 512 bytes. The last 2 bytes will come from the magic number.
    300 times	510 - ($ - $$) db 0
    301 ; Magic number.
    302 dw	0xaa55