summaryrefslogtreecommitdiffstats
path: root/arch/x86/boot/bootloader/setup.s
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/boot/bootloader/setup.s')
-rw-r--r--arch/x86/boot/bootloader/setup.s268
1 files changed, 268 insertions, 0 deletions
diff --git a/arch/x86/boot/bootloader/setup.s b/arch/x86/boot/bootloader/setup.s
new file mode 100644
index 0000000..2afc974
--- /dev/null
+++ b/arch/x86/boot/bootloader/setup.s
@@ -0,0 +1,268 @@
+/*
+ setup.s is second stage bootloader loaded at 0x90200 (by
+ first stage), is responsible for getting the system data
+ from the BIOS.
+
+ System data puts at special place: 0x90000-0x901FF.
+*/
+
+.code16 # Tell GAS to generate 16 bit code
+
+.include "bios.inc"
+.include "rm_seg.inc"
+
+.set ENDSEG, KERNSEG + KERNSIZE # Where to stop loading kernel
+
+# Define some constants for the GDT segment descriptor offsets
+.set CODESEG, gdt_code - gdt_start
+.set DATASEG, gdt_data - gdt_start
+
+# Keyboard Controller commands:
+.set READ_OUTP, 0xD0 # Read Output Port
+.set WRITE_OUTP, 0xD1 # Write Output Port
+
+.global _start # Make the symbol visible to ld
+_start:
+ mov $SDATASEG, %ax
+ mov %ax, %ds
+ mov %ax, %ss
+ mov $KERNSEG, %ax
+ mov %ax, %es
+
+ mov $0xFF00, %bp # Set up the stack at 0x9ff00
+ mov %bp, %sp
+
+ BIOS_PRINT $get_data_msg
+
+ # Get current cursor position
+ mov $0x3, %ah
+ xor %bh, %bh # Page Number = 0
+ int $0x10
+ add $3, %dh # Add number of next BIOS_PRINTs
+ mov %dx, (2) # Save it
+
+ # Get disk drive parameters
+ xor %ax, %ax
+ mov %ax, %es # ES:DI = 0x0000:0x0000 to guard
+ mov %ax, %di # against BIOS bugs
+ mov (0), %dl # Set drive boot
+ mov $0x8, %ah
+ int $0x13
+ jc disk_error
+
+ # Interrupt return:
+ # - CH = low eight bits of maximum cylinder number
+ # - CL = maximum sector number (bits 5-0)
+ # high two bits of maximum cylinder number (bits 7-6)
+ # - DH = maximum head number
+ xor %ch, %ch
+ and $0b00111111, %cl
+ xor %dl, %dl
+ mov %dx, heads
+ mov %cx, sectors
+
+ # TODO: get memory size
+ # TODO: get video infos
+
+load_kernel: # Load our kernel
+ BIOS_PRINT $boot_load_kern_msg
+
+ # Load the system at $KERNSEG address:
+ mov $KERNSEG, %ax
+ mov %ax, %es # ES - starting address segment
+ xor %bx, %bx # BX is offset within segment
+
+ # A few words about the algorithm:
+ # We read 0x10000 bytes (64 kB) and overflow BX (16 bytes register),
+ # then add 0x1000 to ES reg, after that compare with $ENDSEG
+ #
+ # If KERNSIZE != 0x10000 * N we read some unnecessary data, but
+ # i think it's not a problem
+repeat_read:
+ mov %es, %ax
+ cmp $ENDSEG, %ax
+ jae enable_a20 # Jump if AX >= $ENDSEG
+get_sects_for_read:
+ mov sectors, %ax # AX = amount of sectors - current sector
+ sub csect, %ax # AX has 6 significant bytes
+ mov %ax, %cx # Calculate how many bytes we get by
+ # reading AX sectors
+ shl $9, %cx # One sector = 2^9 = 512
+ add %bx, %cx # CX = 0@@@.@@@0.0000.0000 + BX
+ jnc read_sects # if not overflow, then jump
+ jz read_sects # if CX = 0, then jump
+ xor %ax, %ax # AX = 0
+ sub %bx, %ax # AX = amount of sectors that we must
+ shr $9, %ax # read for overflow BX
+read_sects:
+ call read_track # INPUT: AX
+ mov %ax, %cx # CX = amount of sectors that we read
+ add csect, %ax
+ cmp sectors, %ax # Current sector = amount of sectors?
+ jne check_read # If not equal, jump
+ mov chead, %ax
+ cmp heads, %ax # Current head = amount of heads?
+ jne inc_chead # If not equal, jump
+ movw $0xffff, chead # Current head will overflow and equal 0
+ # after INC instuction in inc_chead
+ incw ctrack # Go to next cylinder
+ # We don't check cylinder overflow
+ # because it makes no sense
+inc_chead:
+ incw chead
+ xor %ax, %ax
+check_read:
+ mov %ax, csect # Calculate how many bytes we get by
+ shl $9, %cx # reading AX sectors
+ add %cx, %bx # Add it to BX
+ jnc repeat_read # If BX not overflow, jjmp
+ mov %es, %ax
+ add $0x1000, %ax # We read 0x10000 = 65536 bytes
+ mov %ax, %es
+ xor %bx, %bx
+ jmp repeat_read
+
+# INPUT:
+# AX - amount of sectors that we want to read
+read_track:
+ push %ax
+ push %bx
+ push %cx
+ push %dx
+ mov ctrack, %dx
+ mov csect, %cx # Set sector
+ inc %cx # Add +1 because sector has a base of 1
+ mov %dl, %ch # Set cylinder
+ mov chead, %dh # Set head
+ mov %dl, %dh
+ mov (0), %dl # Set boot
+ mov $0x02, %ah # Set BIOS read sector routine
+ int $0x13
+ jc . # Error :(
+ pop %dx
+ pop %cx
+ pop %bx
+ pop %ax
+ ret
+
+enable_a20:
+ BIOS_PRINT $enable_a20_msg
+
+ cli # Switch of interrupt until we have set
+ # up the protected mode interrupt vector
+
+ call wait_input
+ mov $READ_OUTP, %al
+ out %al, $0x64
+ call wait_output
+
+ in $0x60, %al # Read input buffer and store on stack
+ push %ax
+ call wait_input
+
+ mov $WRITE_OUTP, %al
+ out %al, $0x64
+ call wait_input
+
+ pop %ax # Pop the output port data from stack
+ or $2, %al # Set bit 1 (A20) to enable
+ out %al, $0x60 # Write the data to the output port
+ call wait_input
+
+switch_to_pm:
+ BIOS_PRINT $boot_prot_mode_msg
+ 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, $KERNADDR
+
+disk_error:
+ BIOS_PRINT $disk_error_msg
+ jmp .
+
+wait_input:
+ in $0x64, %al # Read status
+ test $2, %al # Is input buffer full?
+ jnz wait_input # yes - continue waiting
+ ret
+
+wait_output:
+ in $0x64, %al
+ test $1, %al # Is output buffer full?
+ jz wait_output # no - continue waiting
+ ret
+
+# 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
+
+# Total amount of HDD components:
+heads: # 8 significant bytes
+ .word 0x0
+sectors: # 6 significant bytes
+ .word 0x0
+
+# The number of the current component with which we interact:
+ctrack: # track/cylinder
+ .word 0x0
+chead:
+ .word 0x0
+csect:
+ .word 1 + SETUPLEN
+
+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:
+ .word gdt_start, 0x9
+
+get_data_msg:
+ .asciz "Getting the system data from the BIOS\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"
+
+enable_a20_msg:
+ .asciz "Enabling A20 line\r\n"
+
+disk_error_msg:
+ .asciz "Disk read error!"
+
+.space (512 * SETUPLEN) - (. - _start), 0