; ; Bootsector Linux loader 1.3 ; (c) 2023- Alphons van der Heijden alphons@heijden.com ; ; (c) 2014- Dr Gareth Owen (www.ghowen.me). All rights reserved. ; Some code adapted from Sebastian Plotz - rewritten, adding pmode and initrd support. ; This program is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 3 of the License, or ; (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program. If not, see . [BITS 16] org 0x7c00 cli xor ax, ax mov ds, ax mov ss, ax mov sp, 0x7c00 ; setup stack mov ax, 0x2401 ; now get into protected move (32bit) as kernel is large and has to be loaded high int 0x15 ; A20 line enable via BIOS jc err lgdt [gdt_desc] ; Global Descriptor Table mov eax, cr0 or eax, 1 mov cr0, eax mov bx, 0x8 ; first descriptor in GDT mov ds, bx mov es, bx mov gs, bx and al, 0xFE ; back to real mode mov cr0, eax xor ax,ax ; restore segment values - now limits are removed but seg regs still work as normal mov ds, ax mov gs, ax mov ax, 0x1000 ; segment for kernel load (mem off 0x10000) mov es, ax sti ; now in UNREAL mode mov ax, 1 ; one sector xor bx,bx ; offset mov cx, 0x1000 ; seg call hddread read_kernel_setup: mov al, [es:0x1f1] ; no of sectors cmp ax, 0 jne read_kernel_setup.next mov ax, 4 ; default is 4 .next: ; ax = count mov bx, 512 ; next offset mov cx, 0x1000 ; segment call hddread cmp word [es:0x206], 0x204 jb err test byte [es:0x211], 1 jz err mov byte [es:0x210], 0xe1 ; loader type mov byte [es:0x211], 0x81 ; heap use? !! SET Bit5 to Make Kern Quiet mov word [es:0x224], 0xde00 ; head_end_ptr mov byte [es:0x227], 0x01 ; ext_loader_type / bootloader id mov dword [es:0x228], 0x1e000 ; cmd line ptr mov si, cmdLine ; copy cmd line mov di, 0xe000 mov cx, cmdLineLen rep movsb ; copies from DS:si to ES:di (0x1e000) ; modern kernels are bzImage ones (despite name on disk and so ; the protected mode part must be loaded at 0x100000 ; load 127 sectors at a time to 0x2000, then copy to 0x100000 ; load_kernel mov edx, [es:0x1f4] ; bytes to load shl edx, 4 call loader ; load initrd mov eax, 0x7fab000 ; this is the address qemu loads it at mov [highmove_addr],eax ; end of kernel and initrd load address mov [es:0x218], eax mov edx, [initRdSize] ; ramdisk size in bytes mov [es:0x21c], edx ; ramdisk size into kernel header call loader kernel_start: cli mov ax, 0x1000 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax mov sp, 0xe000 jmp 0x1020:0 ; ================= functions ==================== ; length in bytes into edx ; uses hddread [hddLBA] and highmove [highmove_addr] vars ; clobbers 0x2000 segment loader: .loop: cmp edx, 127*512 jl loader.part_2 jz loader.finish mov ax, 127 ; count xor bx, bx ; offset mov cx, 0x2000 ; seg push edx call hddread call highmove pop edx sub edx, 127*512 jmp loader.loop .part_2: ; load less than 127*512 sectors shr edx, 9 ; divide by 512 inc edx ; increase by one to get final sector if not multiple - otherwise just load junk - doesn't matter mov ax, dx xor bx,bx mov cx, 0x2000 call hddread call highmove .finish: ret highmove_addr dd 0x100000 ; source = 0x2000 ; count = 127*512 fixed, doesn't if matter we copy junk at end ; don't think we can use rep movsb here as it wont use EDI/ESI in unreal mode highmove: mov esi, 0x20000 mov edi, [highmove_addr] mov edx, 512*127 mov ecx, 0 ; pointer .loop: mov eax, [ds:esi] mov [ds:edi], eax add esi, 4 add edi, 4 sub edx, 4 jnz highmove.loop mov [highmove_addr], edi ret err: cli ; don't do the jmp $ here hlt ; it will eat your cpu by thermal heat jmp err hddread: push eax mov [dap.count], ax mov [dap.offset], bx mov [dap.segment], cx mov edx, dword [hddLBA] mov dword [dap.lba], edx and eax, 0xffff add edx, eax ; advance lba pointer mov [hddLBA], edx mov ah, 0x42 mov si, dap mov dl, 0x80 ; first hdd int 0x13 jc err pop eax ret dap: db 0x10 ; size db 0 ; unused .count: dw 0 ; num sectors .offset: dw 0 ; dest offset .segment: dw 0 ; dest segment .lba: dd 0 ; lba low bits dd 0 ; lba high bits ;descriptor gdt_desc: dw gdt_end - gdt - 1 dd gdt ; access byte: [present, priv[2] (0=highest), 1, Execbit, Direction=0, rw=1, accessed=0] ; flags: Granuality (0=limitinbytes, 1=limitin4kbs), Sz= [0=16bit, 1=32bit], 0, 0 gdt: dq 0 ; first entry 0 ; flat data segment dw 0FFFFh ; limit[0:15] (aka 4gb) dw 0 ; base[0:15] db 0 ; base[16:23] db 10010010b ; access byte db 11001111b ; [7..4]= flags [3..0] = limit[16:19] db 0 ; base[24:31] gdt_end: ; config options hddLBA dd 1 ; start address for kernel - subsequent calls are sequential initRdSize dd initRdSizeDef ; kernel command line fills up the space, can not exceed address 510 cmdLine db cmdLineDef,0 cmdLineLen equ $-cmdLine ;boot sector magic times 510-($-$$) db 0 dw 0xaa55 ; this is the end