summaryrefslogtreecommitdiffstats
path: root/arch/x86/boot/bootsect.s
blob: 341013125aff31ae71bb6630701c830fdf31ac6b (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
.code16							# Tell GAS to generate 16 bit code

.global _start					# Make the symbol visible to ld

.include "bios.inc"

# Define some constants for the GDT segment descriptor offsets
.set CODESEG, gdt_code - gdt_start
.set DATASEG, gdt_data - gdt_start

.set SYSSEG, 0x1000				# Historical load address
.set MAGIC, 0xAA55

.section .text.bootentry		# Code that will start executing at
								# special address (specified in linker)
_start:
	jmp $0x0, $_start2			# Normalize the start address
								# CS = 0 and IP = _start2
_start2:
	mov %cs, %ax				# AX = CS = 0 (see above)
	mov %ax, %ds				# Zero segment registers
	mov %ax, %es
	mov %ax, %ss
	mov %ax, %sp

	cld							# Set direction flag for incrementing
	mov %dl, boot_drive			# BIOS stores our boot drive in DL,
								# so we remember it

	BIOS_PRINT $boot_real_mode_msg

load_kernel:					# Load our kernel
	BIOS_PRINT $boot_load_kern_msg

	mov $0x02, %ah				# Set BIOS read sector routine
	mov boot_drive, %dl			# Read drive number from $boot_drive
	mov $0x00, %ch				# Select cylinder 0
	mov $0x00, %dh				# Select head 0 [has a base of 0]
	mov $0x02, %cl				# Select sector 2 (next after the
								# boot sector) [has a base of 1]
	mov $0x01, %al				# Read 1 sectors
	mov $SYSSEG, %bx			# Load sectors to ES:BX (0:$SYSSEG)
	int $0x13					# Start reading from drive
	jc disk_error				# If carry flag set, bios failed to read

	# FIXME: we must compare different register
	cmp %al, %al				# If AL(sect. read) != <>(sect. expected)
	jne disk_error				# then return disk error

switch_to_pm:
	BIOS_PRINT $boot_prot_mode_msg
	cli							# Switch of interrupt until we have set
								# up the protected mode interrupt vector
	lgdt gdt_descriptor			# Load our global descriptor table

	mov %cr0, %eax				# Set the first bit of CR0
	or $0x01, %eax				# to make the switch to protected mode
	mov %eax, %cr0

	# Make a far jump to our 32-bit code.
	# This also forces the CPU to flush its cache of pre-fetched
	# and real-mode decoded instructions, which can cause problems
	jmp $CODESEG, $init_pm

disk_error:
	BIOS_PRINT $disk_error_msg
	jmp .

.code32
init_pm:
	mov $DATASEG, %ax			# Point segment registers to the
	mov %ax, %ds				# data selector we defined in our GDT
	mov %ax, %es
	mov %ax, %ss
	mov %ax, %fs
	mov %ax, %gs

	mov $0x90000, %ebp			# Update stack position so it is right
	mov %ebp, %esp				# at the top of the free space.

	jmp . 						# infinite loop

# Global Descriptor Table (contains 8-byte entries)
gdt_start:
gdt_null:						# The mandatory null descriptor
	.quad 0x0

gdt_code:						# The code segment descriptor
	# Base = 0x0, limit = 0xfffff
	# 1st flags: (present)1 (privilege)00 (descriptor type)1 -> b1001
	# Type flags: (code)1 (conforming)0 (readable)1 (accessed)0 -> b1010
	# 2nd flags: (granularity)1 (size)1 (64-bit seg)0 (AVL)0 -> b1100
	.word 0xffff				# Limit (bits 0-15)
	.word 0x0					# Base (bits 0-15)
	.byte 0x0					# Base (bits 16-23)
	.byte 0b10011010			# 1st flags, type flags
	.byte 0b11001111			# 2nd flags, limit (bits 16-19)
	.byte 0x0					# Base (bits 24-31)

gdt_data: 						# the data segment descriptor
	# Same as code segment except for the type flags:
	# Type flags: (code)0 (direction)0 (writable)1, (accessed)0 -> b0010
	# P.S: direction bit: 0 the segment grows up
	.word 0xffff				# Limit (bits 0-15)
	.word 0x0					# Base (bits 0-15)
	.byte 0x0					# Base (bits 16-23)
	.byte 0b10010010			# 1st flags, type flags
	.byte 0b11001111			# 2nd flags, limit (bits 16-19)
	.byte 0x0					# Base (bits 24-31)
gdt_end:

# Global variables
boot_drive:
	.byte 0

gdt_descriptor:
	# The 6-byte GDT structure containing:
	# - GDT size, 2 bytes (size always less one of the real size):
	.word gdt_end - gdt_start - 1
	# - GDT address, 4 bytes:
	.long gdt_start

boot_real_mode_msg:
	.asciz "Started mfsos in 16-bit real mode\r\n"

boot_prot_mode_msg:
	.asciz "Entering 32-bit protected mode\r\n"

boot_load_kern_msg:
	.asciz "Loading kernel into memory\r\n"

disk_error_msg:
	.asciz "Disk read error!"

# Bootsector padding
.space 512 - 2 - (. - _start), 0
.word MAGIC