DEV Community

Jaipal001
Jaipal001

Posted on

64-bit bootloader from scratch in AT&T assembly, Part-7

Btw, save that bootloader in seperate file as bootloader.asm

Bootloader full code

.code16
.org 0

.text
.global _start

_start:
    # Segment setup
    xor %ax, %ax
    mov %ax, %ds
    mov %ax, %es
    mov %ax, %ss

    # Stack setup
    mov $0x9c00, %bp
    mov %bp, %sp

    # Load Kernel from Disk
    mov $0x1000, %ax     # Segment 0x1000
    mov %ax, %es         # ES now points to 0x1000
    xor %bx, %bx         # Offset 0
    # Destination is ES:BX -> (0x1000 * 16) + 0 = 0x10000
    mov $2, %ah              # BIOS read sector function
    mov $8, %al              # Number of sectors to read (adjust as needed)
    xor %ch, %ch             # Cylinder 0
    mov $2, %cl              # Sector 2 (Sector 1 is the bootloader)
    xor %dh, %dh             # Head 0
    int $0x13

    jc disk_error
    jmp disable_int

disk_error:
    mov $disk_err_msg, %dx
    call print
    call println

disable_int:
    cli
    lgdt gdtp
    lidt idt

    mov %cr0, %eax    # This works in 16-bit mode!
    or $0x1, %eax     # Modify the 32-bit register
    mov %eax, %cr0    # Write it back

    movw $(gdt_data_segment - gdt_start), %ax
    movw %ax, %ds
    movw %ax, %es
    movw %ax, %es
    movw %ax, %fs
    movw %ax, %gs
    movw %ax, %ss

    ljmp $(gdt_code_segment - gdt_start), $entry32

print:
    lodsb
print_loop:
    inb (%dx), %al
    cmp $0, %al
    je print_end
    mov $0x0e, %ah
    mov $0x07, %bl
    int $0x10
    add $1, %dx
    jmp print_loop
print_end:
    ret

println:
    mov $0x0e, %ah
    mov $0xa, %al
    int $0x10
    mov $0x20, %al
    int $0x10
    ret

.code32
entry32:
    # Proper 32-bit segment initialization
    movw $(gdt_data_segment - gdt_start), %ax
    movw %ax, %ds
    movw %ax, %es
    movw %ax, %ss

    # 1. Setup Paging Structures
    mov $0x1000, %edi
    mov %edi, %cr3
    xor %eax, %eax
    mov $4096, %ecx
    rep stosl

    mov %cr3, %edi
    movl $0x2003, (%edi)         # PML4 -> PDPT
    movl $0x3003, 0x1000(%edi)    # PDPT -> PD
    movl $0x00000083, 0x2000(%edi)# PD -> 2MB Huge Page (Identity map 0-2MB)

    # 2. Enable PAE (FIXED)
    mov %cr4, %eax
    or $(1 << 5), %eax
    mov %eax, %cr4               # Write back to register

    # 3. Enable Long Mode via EFER
    mov $0xC0000080, %ecx
    rdmsr
    or $(1 << 8), %eax           # LME Bit
    wrmsr

    # 4. Enable Paging
    mov %cr0, %eax
    or $(1 << 31), %eax          # PG Bit
    mov %eax, %cr0

    # 5. Jump to 64-bit
    lgdt gdt64_ptr
    ljmp $0x08, $entry64

.code64
entry64:
    mov $KERNEL_OFFSET, %rax
    call *%rax

print_cli:
    mov $0xb8000, %ecx # VIDEO_MEMORY
print_loop_cli:
    # edx = str
    mov $0, %eax
    movb (%edx), %al   # str[i]
    mov $0x0f, %ah    # white fg, black bg
    cmp $0, %al
    je print_end_cli
    mov %ax, (%ecx)
    add $2, %ebx
    add $2, %ecx
    add $1, %edx
    jmp print_loop_cli
print_end_cli:
    ret

println_cli:
    mov $0xb8000, %ecx # VIDEO_MEMORY

    mov $0x0f0a, %eax  # 0f\n
    add %ebx, %ecx
    mov %eax, (%ecx)
    mov $1, %ebx
    ret


/* GDT */
.align 16
gdtp:
    .word gdt_end - gdt_start - 1
    /* .long (0x07C0 << 4) + gdt */
    .long gdt_start

gdt_start:
    .quad 0x0000000000000000    #   Null Descriptor
gdt_code_segment:
    #    Segment limit  Base address
    .word 0xffff,       0x0000
    .byte 0x00        # Base
    # 1st  flags: (present) 1    (privilege)   0                    0         (descriptor type)1 -> 1001b
    # Type flags: (code)    1    (conforming)  0        (readable)  1         (accessed)       0 -> 1010b
    # 10011010
    .byte 0x9a
    # 2nd  flags: (granularity) 1    (32-bit default) 1    (64-bit segment) 0    (available)   0 -> 1100b
    .byte 0xcf
    .byte 0x00        # Base

gdt_data_segment:
    #    Segment limit  Base address
    .word 0xffff,       0x0000
    .byte 0x00        # Base
    # 1st  flags: (present) 1    (privilege)   0                    0         (descriptor type)1 -> 1001b
    # Type flags: (code)    0    (conforming)  0        (readable)  1         (accessed)       0 -> 1010b
    # 10010010
    .byte 0x92
    # 2nd  flags: (granularity) 1    (32-bit default) 1    (64-bit segment) 0    (available)   0 -> 1100b
    .byte 0xcf
    .byte 0x00        # Base

gdt_end:

# Where docs for 64-bit GDT? IDK, just copy it
# 64-bit GDT
.align 16
gdt64:
    .quad 0                      # Null
    .quad 0x00209a0000000000     # Code: L-bit set, D-bit clear
    .quad 0x0000920000000000     # Data
gdt64_ptr:
    .word . - gdt64 - 1
    .quad gdt64                  # 64-bit base

/* IDT */
idt:
    .word 0
    .long 0

running_kernel: .asciz "Trying to run Kernel"
disk_err_msg:   .asciz "Can't load kernel from disk"

KERNEL_OFFSET: .long 0x2000
BOOT_DRIVE:    .byte 0

.fill 510-(.-_start), 1, 0
.word 0xAA55
Enter fullscreen mode Exit fullscreen mode

Makefile in next par

Top comments (0)