os

Toy OS
git clone git://git.christosmarg.xyz
Log | Files | Refs | README | LICENSE

boot.asm (5975B)


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