os

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

boot.s (6396B)


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