diff options
author | Joursoir <chat@joursoir.net> | 2022-09-27 18:59:00 +0300 |
---|---|---|
committer | Joursoir <chat@joursoir.net> | 2022-10-04 02:34:12 +0300 |
commit | 14c0596d40f5b02153ce379ea6060bd82120f4f9 (patch) | |
tree | 58d633b4aa883c7e9a52cbdf6431263a77458eeb /arch/x86/boot/bootloader/setup.s | |
parent | 3955de98fa26ca847041f5fcb50268927b44a208 (diff) | |
download | mfsos-14c0596d40f5b02153ce379ea6060bd82120f4f9.tar.gz mfsos-14c0596d40f5b02153ce379ea6060bd82120f4f9.tar.bz2 mfsos-14c0596d40f5b02153ce379ea6060bd82120f4f9.zip |
move our bootloader to special directory
Diffstat (limited to 'arch/x86/boot/bootloader/setup.s')
-rw-r--r-- | arch/x86/boot/bootloader/setup.s | 268 |
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 |