cpu 386 bits 16 org 0 %define BIT_READWRITE (1 << 1) %define BIT_EXECUTABLE (1 << 3) %define BIT_ISCODEORDATA (1 << 4) %define BIT_PRESENT (1 << 7) %define PBIT_PRESENT 1 %define PBIT_WRITABLE 2 %define PBIT_USERLEVEL 4 %define PBIT_WRITETHROUGH 8 %define PBIT_CACHEDISABLE 16 %define PBIT_ACCESSED 32 MALLOC_MEM equ 0x520 BOOT1_SEGMENT equ 0x3000 BOOT1_ADDR equ (BOOT1_SEGMENT * 0x10) start: mov [ld_lba.drive + 1], dl mov ah, 8 mov di, 0 int 0x13 mov dl, dh mov dh, 0 inc dx mov [lba2chs.numheads + 1], dx and cl, 0x3F mov ch, 0 mov [lba2chs.spt + 1], cx mov eax, [es:0x7C00 + 0x1BE + 8] mov [get_inode.abs1], eax mov [read_inode.abs1], eax mov ecx, 40 * 25 mov edi, 0 .lup: mov [fs:edi + ecx * 4 - 4], dword 0x0F000F00 dec ecx jnz .lup mov si, MSG_START mov ax, MSG_START.end - MSG_START call print16 mov edi, MALLOC_MEM ;Bump allocator pointer add edi, 4096 mov [TSS.esp0], edi ;End of stack ~ beginning of memory map!! a20: cli call .waito mov al, 0xAD out 0x64, al call .waito mov al, 0xD0 out 0x64, al .waiti: in al, 0x64 test al, 1 jz .waiti in al, 0x60 push ax call .waito mov al, 0xD1 out 0x64, al call .waito pop ax or al, 2 out 0x60, al call .waito mov al, 0xAE out 0x64, al call .waito sti jmp ramdetect .waito: in al, 0x64 test al, 2 jnz .waito ret ramdetect: ;Currently only detects RAM below 16MB, which will be enough for a while. clc int 0x12 jc .fail mov [es:di + 0], dword 0 mov [es:di + 4], dword 0 movzx ecx, ax shr cx, 2 mov [es:di + 8], ecx mov [es:di + 12], byte 0 add di, 13 mov al, 0 add cx, 7 shr cx, 3 rep stosb xor cx, cx xor dx, dx mov ax, 0xE801 int 0x15 jc .fail cmp ah, 0x86 je .fail cmp ah, 0x80 je .fail jcxz .useax mov ax, cx mov bx, dx .useax: mov [es:di + 0], dword 0x100000 mov [es:di + 4], dword 0 movzx ecx, ax shr ecx, 2 mov [es:di + 8], ecx mov [es:di + 12], byte 0 add di, 13 mov al, 0 shr ecx, 3 rep stosb jmp .foundMMap .fail: mov si, MSG_FAIL mov ax, MSG_FAIL.end - MSG_FAIL call print16 jmp $ .foundMMap: mov si, MSG_DETECTED_MEMORY_MAP mov ax, MSG_DETECTED_MEMORY_MAP.end - MSG_DETECTED_MEMORY_MAP call print16 pg: ; Create page directory add edi, 4095 and edi, ~4095 mov cr3, edi ;In advance lea eax, [edi + 4096] or eax, PBIT_PRESENT | PBIT_WRITABLE mov [es:edi], eax add edi, 4 mov eax, 0 mov ecx, 1023 rep stosd mov ecx, 192 mov edx, PBIT_PRESENT | PBIT_WRITABLE .lup: mov [es:edi], edx add edi, 4 add edx, 4096 dec ecx jnz .lup mov ecx, 1024 - 192 mov eax, 0 rep stosd mov eax, cr3 ;Back to the page directory mov [es:eax + 4092], edi or dword [es:eax + 4092], PBIT_PRESENT | PBIT_WRITABLE mov [es:edi + 4092], edi or dword [es:edi + 4092], PBIT_PRESENT | PBIT_WRITABLE add edi, 4096 mov esi, MSG_STEP3s mov eax, MSG_STEP3s.end - MSG_STEP3s call print16 loadkernel: mov eax, 2 ;root call get_inode mov bx, di call read_inode mov eax, edi mov esi, KERNEL_FILE mov ecx, KERNEL_FILE.end - KERNEL_FILE call find_in_dirnode test eax, eax jnz .found mov si, MSG_STEP5f mov ax, MSG_STEP5f.end - MSG_STEP5f call print16 jmp $ .found: call get_inode mov bx, di call read_inode ; Now edi is kernel location, ebx is after kernel. call fix_module_addresses mov [inPE.KERNEL_START], edi mov edi, ebx ;Allocate modules after kernel ;~ mov [inPE.MODULES_START], edi loadmods: mov eax, 2 call get_inode mov esi, [endSector + 32] ;filesize push es push ds pop es mov bx, endSector + 0x200 ;Will break if root directory is larger than 1KB. call read_inode pop es mov eax, endSector + 0x200 .lup: ;Loop over files, the names of which end with ".mod" movzx ecx, word [eax + 10] ;Length of name lea edx, [eax + 12] ;Get name string cmp ecx, 4 ;Test if shorter than 4 characters jb .cont .testext: cmp [eax + ecx + 12 - 4], dword ".mod" jne .cont .testkrnl: cmp [eax + 12], dword "krnl" je .cont .ismod: inc dword [inPE.MOD_COUNT] push eax mov eax, [eax] ;inode call get_inode call page_align_es_edi push esi mov esi, cr3 mov cx, 1024 push ds push word 0 pop ds rep movsd ;Copy page directory to one local to process pop ds pop esi mov bx, di call read_inode ;edi = start, ebx = end mov di, bx pop eax .cont: movzx ecx, byte [eax + 9] shl cx, 4 add eax, ecx sub esi, ecx jnz .lup .end: mov di, es shl edi, 4 add edi, 4095 and edi, ~4095 mov [inPE.IMPORTANT_MEM_END], edi xor ax, ax mov es, ax add ebx, edi fixmmap: ; Make all module memories "used" lea ecx, [es:ebx + 4095] shr ecx, 12 mov ebx, ecx and ebx, 7 shr ecx, 3 mov edi, [TSS.esp0] add edi, 13 push edi mov al, -1 rep stosb mov ecx, ebx mov al, 1 shl al, cl dec al mov [es:edi], al pop edi or [es:edi + (BOOT1_ADDR / 4096 / 8)], byte 3 ; TODO: Let boot1 get overwritten after load modex: mov ax, 0x13 int 0x10 mov dx, 0x3C4 mov al, 4 out dx, al inc dx mov al, 6 out dx, al mov dx, 0x3D4 mov al, 0x14 out dx, al inc dx xor al, al out dx, al dec dx mov al, 0x17 out dx, al inc dx mov al, 0xE3 out dx, al dec dx mov al, 0x11 out dx, al inc dx mov al, 0x2C out dx, al dec dx mov al, 0x06 out dx, al inc dx mov al, 0x0D out dx, al dec dx mov al, 0x07 out dx, al inc dx mov al, 0x3E out dx, al dec dx mov al, 0x10 out dx, al inc dx mov al, 0xEA out dx, al dec dx mov al, 0x11 out dx, al inc dx mov al, 0xAC out dx, al dec dx mov al, 0x12 out dx, al inc dx mov al, 0xDF out dx, al dec dx mov al, 0x15 out dx, al inc dx mov al, 0xE7 out dx, al dec dx mov al, 0x16 out dx, al inc dx mov al, 0x06 out dx, al ; ESP should be zero here. push strict dword BOOT1_ADDR + inPE mov esp, BOOT1_ADDR + 0x10000 + 0xFFFC jmp enterPM enterPM: cli or [GDT.R0CSLIMFLAGS], byte 64 ;Make 32-bit lgdt [GDT] mov eax, cr0 or eax, 1 | (1 << 31) mov cr0, eax jmp 8:dword BOOT1_ADDR + .inPM .inPM: bits 32 mov eax, 0x23 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov al, 0x10 mov ss, ax ret enterRM: and [GDT.R0CSLIMFLAGS], byte ~64 ;Make 16-bit lgdt [GDT] jmp 8:.in16 .in16: bits 16 mov eax, cr0 and eax, ~(1 | (1 << 31)) mov cr0, eax jmp 0:.inRM .inRM: mov ax, 0 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax sti ret ; Makes es:edi a normalized far pointer normalize_es_edi: push eax push edi mov ax, es shr di, 4 add ax, di mov es, ax pop edi and edi, 15 pop eax ret page_align_es_edi: push eax xor eax, eax mov ax, es shl eax, 4 add eax, edi add eax, 4095 and eax, ~4095 shr eax, 4 mov es, ax pop eax xor edi, edi ret print16: push di push cx push ax .ind: mov di, 0 mov cx, ax .lup: mov al, [ds:si] mov [fs:di], al inc si inc di inc di dec cx jnz .lup mov [.ind + 1], di ;SMC pop ax pop cx pop di ret ld_lba: pushad call lba2chs mov ch, al mov dh, dl .drive: mov dl, 0 mov ah, 2 mov al, 1 int 0x13 popad ret ; In: AX = lba ; Out: CX = sector, AX = cylinder, DX = head lba2chs: push bx .spt: mov bx, 0xF157 xor dx, dx div bx mov cx, dx inc cx .numheads: mov bx, 0x9001 xor dx, dx div bx pop bx ret ; In: EAX = inode ; Out: *BOOT1_SEGMENT:endSector = inode struct get_inode: push eax push ebx .abs1 equ ($ + 2) add eax, strict dword 0xABCDEF42 push es push ds pop es mov bx, endSector call ld_lba pop es pop ebx pop eax ret ; In: *BOOT1_SEGMENT:endSector = inode struct ; Out: *ES:BX = data read_inode: push edx push ecx push eax push esi push edi push ebx mov dl, [endSector + 8] ;extent count mov ecx, endSector + 104 ;extent starts .lup: mov eax, [ecx] ;start mov esi, [ecx + 48] ;size of extent .abs1 equ ($ + 2) add eax, strict dword 0x12345678 .lup2: call ld_lba add bx, 512 inc eax dec esi jnz .lup2 add ecx, 8 dec dl jnz .lup mov edi, [esp] ;dest address lea esi, [edi + 176] test dword [endSector + 28], 1 << 19 jz .doesntHaveInline add esi, 512 - 176 .doesntHaveInline: mov ecx, [endSector + 32] ;filesize push ds push es pop ds rep movsb pop ds add esp, 4 ;ignore old ebx, because read_inode should return the addr after pop edi pop esi pop eax pop ecx pop edx ret ; In: *ES:EAX = directory content buffer, *ESI = name to test, ECX = name length ; Out: EAX = inode or 0 if not found find_in_dirnode: push ebx push edx push edi mov edi, [endSector + 32] ;filesize .lup: movzx edx, word [es:eax + 10] ;record name length cmp dx, cx .lup2: dec dx mov bl, [es:eax + 12 + edx] cmp bl, [esi + edx] jne .next test dx, dx jnz .lup2 jmp .found .next: movzx edx, byte [es:eax + 9] ;record length shl dx, 4 add eax, edx sub edi, edx jnz .lup mov eax, 0 jmp .end .found: mov eax, [es:eax] .end: pop edi pop edx pop ebx ret fix_module_addresses: push esi push ecx push eax push ebx movzx ecx, word [es:edi + 4] ;amount of syms lea esi, [es:edi + 12 + ecx * 4] lea esi, [es:esi + ecx * 2] movzx ecx, word [es:edi + 6] ;amount of relocs mov eax, esi ;ptr to relocs lea esi, [esi + ecx * 4] ;ptr to data add esi, 15 and esi, ~15 mov [es:edi], esi ;Replace the MOD\0 magic header with the base address test ecx, ecx ;If none, end jz .end .lup: ; Apply relocation mov ebx, [es:eax] add ebx, esi add [es:ebx], esi add eax, 4 dec ecx jnz .lup .end: pop ebx pop eax pop ecx pop esi ret bits 32 inPE: mov [BOOT1_ADDR + TSS.ss0], dword 0x10 mov esp, [BOOT1_ADDR + TSS.esp0] mov eax, 40 ltr ax mov ax, [BOOT1_ADDR + print16.ind + 1] mov [BOOT1_ADDR + print32.LAST], ax mov esi, BOOT1_ADDR + MSG_STEP2s mov eax, MSG_STEP2s.end - MSG_STEP2s call print32 .KERNEL_START equ ($ + 1) mov edi, strict dword 0 lea ebx, [edi + 14] ; Sym 0: ppm_Bitmap mov eax, [ebx] add eax, [edi] ;Base address that used to be magic header push dword [BOOT1_ADDR + TSS.esp0] ;As said before, end of stack ~ beginning of mmap! pop dword [eax] add ebx, 6 ; Sym 1: vpm_init mov eax, [ebx] add eax, [edi] .IMPORTANT_MEM_END equ ($ + 1) push strict dword 0 call eax add esp, 4 add ebx, 6 ; Sym 2: vpm_map mov eax, [ebx] add eax, [edi] mov [BOOT1_ADDR + .VPM_MAP_ENTRY], eax add ebx, 6 ; Sym 3: canal_init mov eax, [ebx] add eax, [edi] call eax add ebx, 6 ; Sym 4: pci_init mov eax, [ebx] add eax, [edi] call eax add ebx, 6 ; Sym 5: scheduler_init mov eax, [ebx] add eax, [edi] call eax add ebx, 6 ; Sym 6: scheduler_spawn mov eax, [ebx] add eax, [edi] mov [BOOT1_ADDR + .SCHEDULER_SPAWN_ENTRY], eax add ebx, 6 ; Sym 7: scheduler_start mov eax, [ebx] add eax, [edi] mov [BOOT1_ADDR + .SCHEDULER_START_ENTRY], eax .MOD_COUNT equ ($ + 1) mov ebp, strict dword 0 .modspawnlup: ; Find next module movzx ecx, word [edi + 4] lea eax, [edi + ecx * 4] lea eax, [eax + ecx * 2] movzx ecx, word [edi + 6] lea eax, [eax + ecx * 4] add eax, 15 and eax, ~15 mov ecx, [edi + 8] lea eax, [eax + ecx] add eax, 4096 + 4095 and eax, ~4095 mov edi, eax mov ebx, 0x40000000 call rebase_module32 sub edx, edi push strict dword edx push strict dword edi push strict dword 0x40000000 push strict dword 1 ;user lea eax, [edi - 4096] ; cr3 push eax .VPM_MAP_ENTRY equ ($ + 1) mov eax, strict dword 0 call eax add esp, 20 push strict dword 0 .SCHEDULER_SPAWN_ENTRY equ ($ + 1) mov eax, strict dword 0 call eax add esp, 4 lea ebx, [edi - 4096] mov [eax + 46], ebx ;cr3 mov ebx, [edi + 14] ;get sym 0 (modentry) add ebx, [edi] ;add data offset mov [eax + 34], ebx ;eip dec ebp jnz .modspawnlup .SCHEDULER_START_ENTRY equ ($ + 1) mov eax, strict dword 0 jmp eax print32: push ebx push eax push ecx movzx ebx, word [BOOT1_ADDR + .LAST] mov ecx, eax .lup: mov al, [esi] mov [0xB8000 + ebx], al inc esi inc ebx inc ebx dec ecx jnz .lup mov [BOOT1_ADDR + .LAST], bx pop ecx pop eax pop ebx ret .LAST: dw 0 ; In: EDI = module pointer, EBX = rebase address ; Out: EDX = physical module end rebase_module32: push ecx push eax push ebx push ebp movzx ecx, word [edi + 4] lea eax, [edi + 12 + ecx * 2] lea eax, [eax + ecx * 4] ; eax = relocations pointer movzx ecx, word [edi + 6] ;ecx = amount of relocations lea edx, [eax + ecx * 4] ; edx = data pointer add edx, 15 and edx, ~15 mov [edi], edx ;Store offset of data buffer in sub [edi], edi ;file in place of magic header add [edi], ebx ; mov ebx, [edi] test ecx, ecx jz .end .lup: mov ebp, [eax] add [edx + ebp], ebx add eax, 4 dec ecx jnz .lup .end: add edx, [edi + 8] pop ebp pop ebx pop eax pop ecx ret MSG_FAIL: db "Failed to detect memory map. " .end: MSG_START: db "Starting eklernel. " .end: MSG_DETECTED_MEMORY_MAP: db "Found memory map. " .end: MSG_STEP2s: db "Entered protected mode. " .end: MSG_STEP3s: db "Generated paging structures. " .end: MSG_STEP4f: db "Boot filesystem must have block size of 1024. " .end: KERNEL_FILE: db "krnl.mod" .end: MSG_STEP5f: db "Kernel not found; aborting boot. " .end: GDT: dw (GDT.END - GDT) - 1 ;GDT descriptor in the null entry dd BOOT1_ADDR + GDT dw 0 dw 0xFFFF, 0x0000 db 0x00, BIT_READWRITE | BIT_EXECUTABLE | BIT_ISCODEORDATA | BIT_PRESENT | (0 << 5) .R0CSLIMFLAGS: db (128 | 64) | 0xF db 0 dw 0xFFFF, 0x0000 db 0x00, BIT_READWRITE | BIT_ISCODEORDATA | BIT_PRESENT | (0 << 5) db (128 | 64) | 0xF db 0 dw 0xFFFF, 0x0000 db 0x00, BIT_READWRITE | BIT_EXECUTABLE | BIT_ISCODEORDATA | BIT_PRESENT | (3 << 5) db (128 | 64) | 0xF db 0 dw 0xFFFF, 0x0000 db 0x00, BIT_READWRITE | BIT_ISCODEORDATA | BIT_PRESENT | (3 << 5) db (128 | 64) | 0xF db 0 dw (TSS.END - TSS) - 1 dw TSS db BOOT1_ADDR / 0x10000, 1 | 8 | 128 db 0, 0 .END: TSS: dd 0 .esp0: dd 0 .ss0: dd 0 times 23 dd 0 .END: end: times (end - start + 511) / 512 * 512 - ($ - $$) db 0 endSector: