My GDT:
Code: Select all
ALIGN 8
gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps
; the GDT starts with a null 8-byte
dd 0x0 ; 4 byte
dd 0x0 ; 4 byte
; GDT for kernel code segment. base = 0x00000000, length = 0xfffff
; for flags, refer to os-dev.pdf document, page 36
gdt_code:
dw 0xffff ; segment length, bits 0-15 (16-bit value)
dw 0x0 ; segment base, bits 0-15 (16-bit value)
db 0x0 ; segment base, bits 16-23 (8-bit value)
db 10011010b ; flags (8 bits)
db 11001111b ; flags (4 bits) + segment length, bits 16-19
db 0x0 ; segment base, bits 24-31
; GDT for kernel data segment. base and length identical to code segment
; some flags changed, again, refer to os-dev.pdf
gdt_data:
dw 0xffff
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
; Base = 0
; Limit = 0xFFFFF
; Access Byte = 0xFA
; Flags = 0xC
gdt_user_code:
dw 0xffff
dw 0x0
db 0x0
db 10011010b
db 11001111b
db 0x0
; Base = 0
; Limit = 0xFFFFF
; Access Byte = 0xF2
; Flags = 0xC
gdt_user_data:
dw 0xffff
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
; gdt taask segment
gdt_tss:
dw 0x67
dw 0x0
db 0x0
db 10001001b
db 0x0
db 0x0
gdt_end:
; GDT descriptor
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size
dd gdt_start ; address (32 bit)
; define some constants for later use
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
Code: Select all
[bits 16]
[org 0x7c00]
; print hello world
mov ah, 0x0e
mov al, 'H'
int 0x10
mov al, 'e'
int 0x10
mov al, 'l'
int 0x10
mov al, 'l'
int 0x10
mov al, 'o'
int 0x10
KERNEL_OFFSET equ 0x10000 ; The same one we used when linking the kernel
mov bx, 0x1000 ; Read from disk and store in 0x1000
mov es, bx
mov bx, 0x0000;es:bx = 0x1000:0x0000 = 0x10000
mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read'
mov al, 50 ; al <- number of sectors to read (0x01 .. 0x80)
mov cl, 0x02 ; cl <- sector (0x01 .. 0x11)
mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl')
mov dh, 0x00 ; dh <- head number (0x0 .. 0xF)
int 0x13 ; BIOS interrupt
mov bp, 0x9000
mov sp, bp
call switch_to_32bit ; disable interrupts, load GDT, etc. Finally jumps to 'BEGIN_PM'
jmp $ ; Never executed
%include "gdt.asm"
%include "a20.asm"
[bits 16]
switch_to_32bit:
call enable_a20 ; 0. enable A20 line
cli ; 1. disable interrupts
nop ; 1.1. some CPUs require a delay after cli
lgdt [gdt_descriptor] ; 2. load the GDT descriptor
mov eax, cr0
or eax, 0x1 ; 3. set 32-bit mode bit in cr0
mov cr0, eax
jmp CODE_SEG:init_32bit ; 4. far jump by using a different segment
[bits 32]
init_32bit: ; we are now using 32-bit instructions
mov ax, DATA_SEG ; 5. update the segment registers
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000 ; 6. update the stack right at the top of the free space
mov esp, ebp
call BEGIN_32BIT ; 7. Call a well-known label with useful code
[bits 32]
BEGIN_32BIT:
call 0x10000 ; Give control to the kernel
jmp $ ; Stay here when the kernel returns control to us (if ever)
BOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten
times 446 - ($-$$) db 0
partition_1:
db 0x80 ; Drive attribute
db 0x00, 0x01, 0x01 ; CHS address of partition start
db 0xFF ; Partition type
db 0x00, 0x0F, 0x12 ; CHS address of partition end
dd 0x00000001 ; LBA of partition start
dd 0x000005A0 ; Number of sectors in partition
; padding
times 16 db 0 ; Entry 2
times 16 db 0 ; Entry 3
times 16 db 0 ; Entry 4
dw 0xaa55