From 894e61508a09841f9b4a8e81fdd265b2a9eef1fc Mon Sep 17 00:00:00 2001 From: Joursoir Date: Tue, 28 Sep 2021 00:35:39 +0000 Subject: x86/boot: transform one-stage to two-stage bootloader bootsect.s is first stage bootloader, it loads setup.s setup.s is second stage bootloader, it saves system data and loads kernel to $KERNADDR, then jump to 32-bit startup code --- arch/x86/boot/bootsect.s | 128 +++++++++-------------------------------------- arch/x86/boot/setup.s | 111 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 104 deletions(-) create mode 100644 arch/x86/boot/setup.s diff --git a/arch/x86/boot/bootsect.s b/arch/x86/boot/bootsect.s index 3410131..a6e274e 100644 --- a/arch/x86/boot/bootsect.s +++ b/arch/x86/boot/bootsect.s @@ -1,137 +1,57 @@ -.code16 # Tell GAS to generate 16 bit code +/* + bootsect.s is loaded at 0x7c00 (BIOS likes always to load the boot + sector to this address). It is first stage bootloader, it has one + task - loads setup.s (second stage) to 0x90000 and jumps there. +*/ -.global _start # Make the symbol visible to ld +.code16 # Tell GAS to generate 16 bit code .include "bios.inc" +.include "rm_seg.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) +.global _start # Make the symbol visible to ld _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 $SDATASEG, %ax # We will store the boot drive at + mov %ax, %ds # $SDATASEG in the first byte + mov $SETUPSEG, %ax # Do it for disk read routine (see below) 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, + mov %dl, (0) # BIOS stores our boot drive in DL, # so we remember it + mov %cs, %ax # AX = CS = 0 (see above) + mov %ax, %ds # Zero data segment register - BIOS_PRINT $boot_real_mode_msg - -load_kernel: # Load our kernel - BIOS_PRINT $boot_load_kern_msg +load_setup: + BIOS_PRINT $load_setup_msg # The routine uses only AX register 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) + mov $SETUPLEN, %al # Read $SETUPLEN sectors + mov $0x0, %bx # Load sectors to ES:BX ($SETUPSEG:0) 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 + # Make a far jump to our setup code + jmp $SETUPSEG, $0x0 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" +load_setup_msg: + .asciz "Loading setup sectors\r\n" disk_error_msg: .asciz "Disk read error!" # Bootsector padding .space 512 - 2 - (. - _start), 0 -.word MAGIC +boot_flag: + .word 0xAA55 diff --git a/arch/x86/boot/setup.s b/arch/x86/boot/setup.s new file mode 100644 index 0000000..2a0ab15 --- /dev/null +++ b/arch/x86/boot/setup.s @@ -0,0 +1,111 @@ +/* + 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" + +# Define some constants for the GDT segment descriptor offsets +.set CODESEG, gdt_code - gdt_start +.set DATASEG, gdt_data - gdt_start + +.global _start # Make the symbol visible to ld +_start: + mov $SDATASEG, %ax + mov %ax, %ds + mov $KERNSEG, %ax + mov %ax, %es + + BIOS_PRINT $get_data_msg + # TODO: get memory size + # TODO: get video infos + +load_kernel: # Load our kernel + BIOS_PRINT $boot_load_kern_msg + + mov $0x02, %ah # Set BIOS read sector routine + mov (0), %dl # Read drive boot + mov $0x00, %ch # Select cylinder 0 + mov $0x00, %dh # Select head 0 [has a base of 0] + mov $0x02+SETUPLEN, %cl # Select sector 2 (next after the + # boot sector) [has a base of 1] + mov $0x10, %al # Read 16 sectors + mov $0x00, %bx # Load sectors to ES:BX ($KERNSEG:0) + int $0x13 # Start reading from drive + jc disk_error # If carry flag set, bios failed to read + +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, $KERNADDR + +disk_error: + BIOS_PRINT $disk_error_msg + jmp . + +# 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 +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" + +disk_error_msg: + .asciz "Disk read error!" + +.space (512 * SETUPLEN) - (. - _start), 0 -- cgit v1.2.3-18-g5258