567 lines
13 KiB
NASM
567 lines
13 KiB
NASM
|
|
%if 0
|
|
|
|
Multiboot header and loader
|
|
2008--2019 by C. Masloch
|
|
|
|
Usage of the works is permitted provided that this
|
|
instrument is retained with the works, so that any entity
|
|
that uses the works is notified of this instrument.
|
|
|
|
DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.
|
|
|
|
%endif
|
|
|
|
%macro mbootchecksummed 1-2.nolist 1BADB002h
|
|
dd %2 ; signature
|
|
dd %1 ; flags
|
|
dd -((%2+%1) & 0FFFF_FFFFh)
|
|
; checksum: dword sum of these three = 0
|
|
%endmacro
|
|
|
|
%macro mboot2checksummed 0-1.nolist 0E852_50D6h
|
|
dd %1 ; signature
|
|
dd 0 ; platform (i386)
|
|
dd mboot2_header_end - mboot2_header
|
|
; size of header, including header tags
|
|
dd -((%1 + (mboot2_header_end - mboot2_header))) & 0FFFF_FFFFh
|
|
; checksum
|
|
%endmacro
|
|
|
|
|
|
MULTIBOOT_CMDLINE_LENGTH equ lsvclBufferLength
|
|
|
|
MULTIBOOT_BASE equ (1024+64)*1024 ; one paragraph above the HMA
|
|
MULTIBOOT_END equ MULTIBOOT_BASE + (payload.actual_end - $$ + 0)
|
|
MULTIBOOT_BSS_END equ MULTIBOOT_END \
|
|
+ fromdwords(dwords(MULTIBOOT_CMDLINE_LENGTH))
|
|
MULTIBOOT_TARGET_SEGMENT equ 200h
|
|
MULTIBOOT_TARGET equ MULTIBOOT_TARGET_SEGMENT << 4
|
|
MULTIBOOT_BPB equ MULTIBOOT_TARGET - 512 - 16
|
|
MULTIBOOT_CMDLINE_START equ MULTIBOOT_BPB + lsvCommandLine.start
|
|
MULTIBOOT_STACK_TOP equ MULTIBOOT_CMDLINE_START
|
|
|
|
%if _MULTIBOOT1
|
|
; Multiboot header
|
|
; must be dword aligned and in first 8 KiB!
|
|
align 4, db 0
|
|
mbootheader:
|
|
mbootchecksummed 00010000h
|
|
; flags: provides info where to load image
|
|
dd MULTIBOOT_BASE + mbootheader ; load address of header
|
|
dd MULTIBOOT_BASE ; load address
|
|
dd MULTIBOOT_END ; load area end (0 = load whole file)
|
|
dd MULTIBOOT_BSS_END ; uninitialised area end (0 = none)
|
|
dd MULTIBOOT_BASE + mbootentry ; entry point
|
|
%endif
|
|
|
|
%if _MULTIBOOT2
|
|
; Multiboot2 header
|
|
; qword (64-bit) aligned and in first 32 KiB
|
|
align 8, db 0
|
|
mboot2_header:
|
|
mboot2checksummed
|
|
mboot2_tag_information_request:
|
|
.:
|
|
.type: dw 1
|
|
.flags: dw 0
|
|
.size: dd .end - .
|
|
dd 1 ; command line
|
|
dd 5 ; ROM-BIOS boot device
|
|
.end:
|
|
align 8, db 0
|
|
mboot2_tag_addresses:
|
|
.:
|
|
.type: dw 2
|
|
.flags: dw 0
|
|
.size: dd .end - .
|
|
dd MULTIBOOT_BASE + mboot2_header
|
|
; load address of header
|
|
dd MULTIBOOT_BASE ; load address
|
|
dd MULTIBOOT_END ; load area end (0 = load whole file)
|
|
dd MULTIBOOT_BSS_END ; uninitialised area end (0 = none)
|
|
.end:
|
|
align 8, db 0
|
|
mboot2_tag_entrypoint:
|
|
.:
|
|
.type: dw 3
|
|
.flags: dw 0
|
|
.size: dd .end - .
|
|
dd MULTIBOOT_BASE + mboot2entry ; entry point
|
|
.end:
|
|
align 8, db 0
|
|
mboot2_tag_end:
|
|
.:
|
|
.type: dw 0
|
|
.flags: dw 0
|
|
.size: dd .end - .
|
|
.end:
|
|
mboot2_header_end:
|
|
%endif
|
|
|
|
|
|
%unmacro mbootchecksummed 1-2.nolist 1BADB002h
|
|
%unmacro mboot2checksummed 0-1.nolist 0E85250D6h
|
|
|
|
%if ($-$$) > 8192
|
|
%fatal Multiboot header must be in the first 8 KiB
|
|
%endif
|
|
|
|
struc MBI
|
|
mbiFlags: resd 1
|
|
resb 8
|
|
mbiBootDevice: resd 1
|
|
mbiCmdLine: resd 1
|
|
mbiModuleCount: resd 1
|
|
mbiModuleTable: resd 1
|
|
; (More data follows but none which seems useful.)
|
|
endstruc
|
|
|
|
[cpu 386]
|
|
[bits 32]
|
|
|
|
numdef MULTIBOOT_CHECKS, 1
|
|
numdef MULTIBOOT_DEBUG, 0
|
|
numdef MULTIBOOT_HALT, 0
|
|
numdef MULTIBOOT_PROGRESS, 0
|
|
|
|
|
|
%if _MULTIBOOT_CHECKS || _MULTIBOOT_HALT
|
|
mbootentry.halt:
|
|
mboot2entry.halt:
|
|
; Unfortunately, as the environment is unknown, we can only
|
|
; literally halt here, as we don't know how to do I/O.
|
|
cli
|
|
@@:
|
|
hlt
|
|
jmp short @B
|
|
%endif
|
|
|
|
%if _MULTIBOOT1
|
|
; INP: eax = 2BADB002h
|
|
; ebx-> multiboot info structure
|
|
; STT: loaded and executed according to Multiboot header, ie
|
|
; loaded at MULTIBOOT_BASE
|
|
; eip = MULTIBOOT_BASE+mbootentry
|
|
; in PM32 with paging disabled
|
|
; CPL 0
|
|
; cs,ds,es,fs,gs,ss = flat 32-bit code/data selectors
|
|
; DI
|
|
; ! stack not set up
|
|
; ! GDT might not be set up
|
|
; ! IDT might not be set up
|
|
; A20 on
|
|
;
|
|
; Note: Although A20 is on, HMA access without an XMM
|
|
; to manage it might be undesirable. Multiboot
|
|
; also doesn't tell us whether and if so then
|
|
; how we can switch A20.
|
|
mbootentry:
|
|
%if _MULTIBOOT_HALT
|
|
jmp .halt
|
|
%endif
|
|
%if _MULTIBOOT_CHECKS
|
|
[bits 16]
|
|
test ax, 0 ; (test eax if in 32-bit segment)
|
|
jmp short .halt ; (skipped if in 32-bit segment)
|
|
[bits 32]
|
|
cmp eax, 2BADB002h ; signature ?
|
|
jne short .halt ; no -->
|
|
smsw eax
|
|
rol eax, 1 ; 2 = protection, 1 = paging
|
|
and al, 3 ; mask off others
|
|
cmp al, 2 ; PE but not PG ?
|
|
jne short .halt ; no -->
|
|
mov eax, cs
|
|
and al, 3 ; CPL 0 ?
|
|
jnz short .halt ; no -->
|
|
%endif
|
|
|
|
%if _MULTIBOOT_PROGRESS
|
|
mov ebp, 0B8000h + 2 * 80 * 20
|
|
mov word [ebp], 5000h | '1'
|
|
inc ebp
|
|
inc ebp
|
|
%endif
|
|
|
|
; prepare this and that
|
|
or edx, -1
|
|
; locate boot drive in Multiboot info structure
|
|
test byte [ ebx + mbiFlags ], 2 ; boot device info valid ?
|
|
jz @F
|
|
mov eax, [ ebx + mbiBootDevice ]; get the info
|
|
rol eax, 16
|
|
xchg al, ah
|
|
mov edx, eax ; dl = boot load unit (or 0FFh),
|
|
; dh = partition (or 0FFh)
|
|
; (edxh = subpartition numbers)
|
|
@@:
|
|
|
|
%if _MULTIBOOT_PROGRESS
|
|
mov word [ebp], 5000h | '2'
|
|
inc ebp
|
|
inc ebp
|
|
%endif
|
|
|
|
xor eax, eax
|
|
mov edi, MULTIBOOT_END
|
|
mov ecx, MULTIBOOT_CMDLINE_LENGTH / 4
|
|
test byte [ ebx + mbiFlags ], 4 ; command line valid ?
|
|
jz @F
|
|
mov esi, [ ebx + mbiCmdLine ]
|
|
rep movsd
|
|
mov byte [ edi - 1 ], 0 ; insure it is terminated
|
|
; (this may truncate the line)
|
|
@@:
|
|
rep stosd
|
|
|
|
%if _MULTIBOOT_PROGRESS
|
|
mov word [ebp], 5000h | '3'
|
|
inc ebp
|
|
inc ebp
|
|
%endif
|
|
|
|
%if _MULTIBOOT2
|
|
jmp multiboot_1_2_common
|
|
%endif
|
|
%endif
|
|
|
|
%if _MULTIBOOT2
|
|
; INP: eax = 36D7_6289h
|
|
; ebx-> multiboot2 info structure
|
|
; STT: loaded and executed according to Multiboot2 header, ie
|
|
; loaded at MULTIBOOT_BASE
|
|
; eip = MULTIBOOT_BASE+mboot2entry
|
|
; in PM32 with paging disabled
|
|
; CPL 0
|
|
; cs,ds,es,fs,gs,ss = flat 32-bit code/data selectors
|
|
; DI
|
|
; ! stack not set up
|
|
; ! GDT might not be set up
|
|
; ! IDT might not be set up
|
|
; A20 on
|
|
;
|
|
; Note: Although A20 is on, HMA access without an XMM
|
|
; to manage it might be undesirable. Multiboot
|
|
; also doesn't tell us whether and if so then
|
|
; how we can switch A20.
|
|
mboot2entry:
|
|
%if _MULTIBOOT_HALT
|
|
jmp .halt
|
|
%endif
|
|
%if _MULTIBOOT_CHECKS
|
|
[bits 16]
|
|
test ax, 0 ; (test eax if in 32-bit segment)
|
|
jmp short .halt ; (skipped if in 32-bit segment)
|
|
[bits 32]
|
|
cmp eax, 36D7_6289h ; signature ?
|
|
jne .halt ; no -->
|
|
smsw eax
|
|
rol eax, 1 ; 2 = protection, 1 = paging
|
|
and al, 3 ; mask off others
|
|
cmp al, 2 ; PE but not PG ?
|
|
jne .halt ; no -->
|
|
mov eax, cs
|
|
and al, 3 ; CPL 0 ?
|
|
jnz .halt ; no -->
|
|
%endif
|
|
|
|
%if _MULTIBOOT_PROGRESS
|
|
mov ebp, 0B8000h + 2 * 80 * 20
|
|
mov word [ebp], 5000h | 'A'
|
|
inc ebp
|
|
inc ebp
|
|
%endif
|
|
|
|
or edx, -1 ; initialise boot partition info
|
|
|
|
xor eax, eax
|
|
mov edi, MULTIBOOT_END
|
|
mov ecx, MULTIBOOT_CMDLINE_LENGTH / 4
|
|
rep stosd ; initialise command line buffer
|
|
|
|
add ebx, 8 ; skip fixed header (size, reserved)
|
|
.tags_loop:
|
|
|
|
%if _MULTIBOOT_PROGRESS
|
|
mov word [ebp], 5000h | 'B'
|
|
inc ebp
|
|
inc ebp
|
|
%endif
|
|
|
|
mov eax, dword [ebx]
|
|
test eax, eax ; end of tags ?
|
|
jz .tags_end ; yes -->
|
|
|
|
cmp eax, 1
|
|
je .cmdline
|
|
|
|
cmp eax, 5
|
|
je .partition
|
|
|
|
.tags_next:
|
|
%if _MULTIBOOT_PROGRESS
|
|
mov word [ebp], 5000h | 'C'
|
|
inc ebp
|
|
inc ebp
|
|
%endif
|
|
|
|
add ebx, dword [ebx + 4]
|
|
; -> after end of tag
|
|
; (at padding or next tag)
|
|
add ebx, 7
|
|
and ebx, ~7 ; skip any padding
|
|
jmp .tags_loop
|
|
|
|
.cmdline:
|
|
|
|
%if _MULTIBOOT_PROGRESS
|
|
mov word [ebp], 5000h | 'D'
|
|
inc ebp
|
|
inc ebp
|
|
%endif
|
|
|
|
lea esi, [ebx + 8]
|
|
mov edi, MULTIBOOT_END
|
|
mov ecx, MULTIBOOT_CMDLINE_LENGTH / 4
|
|
rep movsd ; copy command line
|
|
mov byte [ edi - 1 ], 0
|
|
; insure it is terminated
|
|
; (this may truncate the line)
|
|
jmp .tags_next
|
|
|
|
.partition:
|
|
|
|
%if _MULTIBOOT_PROGRESS
|
|
mov word [ebp], 5000h | 'E'
|
|
inc ebp
|
|
inc ebp
|
|
%endif
|
|
|
|
mov eax, dword [ebx + 8]
|
|
cmp eax, 100h
|
|
jae @F
|
|
mov dl, al ; get ROM-BIOS unit
|
|
@@:
|
|
|
|
mov eax, dword [ebx + 12]
|
|
cmp eax, 100h
|
|
jae @F
|
|
mov dh, al ; get partition number (0 = first primary)
|
|
@@:
|
|
jmp .tags_next
|
|
|
|
.tags_end:
|
|
xor eax, eax
|
|
|
|
%if _MULTIBOOT_PROGRESS
|
|
mov word [ebp], 5000h | 'F'
|
|
inc ebp
|
|
inc ebp
|
|
%endif
|
|
|
|
%endif
|
|
|
|
multiboot_1_2_common:
|
|
mov esp, MULTIBOOT_STACK_TOP ; set a valid stack, at 8 KiB
|
|
; (also required by 86M entry later)
|
|
|
|
%if _MULTIBOOT_PROGRESS
|
|
mov word [ebp], 5000h | 's'
|
|
inc ebp
|
|
inc ebp
|
|
%endif
|
|
|
|
; A20 is on. set up some things
|
|
xor ecx, ecx
|
|
mov edi, 1024*1024
|
|
mov cl, 10h ; (ecx = 10h)
|
|
rep stosd ; clear this area so A20 tests succeed
|
|
; The above writes to 10_0000h..10_003Fh, leaving edi = 10_0040h
|
|
mov al, 0C0h ; (eax = C0h)
|
|
mov byte [ edi-40h+eax ], 0EAh
|
|
mov [ edi-40h+eax+1 ], eax ; write jump for CP/M entry hack here
|
|
; The above writes to 10_00C0h..10_00C4h
|
|
|
|
%if _MULTIBOOT_PROGRESS
|
|
mov word [ebp], 5000h | 't'
|
|
inc ebp
|
|
inc ebp
|
|
%endif
|
|
|
|
; relocate image to a good location, 2000h
|
|
mov esi, MULTIBOOT_BASE ; -> iniload image
|
|
mov edi, MULTIBOOT_TARGET
|
|
mov ecx, (payload.actual_end - $$ + 0 + 3) / 4
|
|
rep movsd
|
|
|
|
%if _MULTIBOOT_PROGRESS
|
|
mov word [ebp], 5000h | 'u'
|
|
inc ebp
|
|
inc ebp
|
|
%endif
|
|
|
|
MULTIBOOT_ESI_AFTER equ MULTIBOOT_BASE + (payload.actual_end - $$ + 0 + 3) / 4 * 4
|
|
%if MULTIBOOT_ESI_AFTER != MULTIBOOT_END
|
|
mov esi, MULTIBOOT_END
|
|
%endif
|
|
mov edi, MULTIBOOT_CMDLINE_START
|
|
mov ecx, MULTIBOOT_CMDLINE_LENGTH / 4
|
|
rep movsd
|
|
|
|
%if _MULTIBOOT_PROGRESS
|
|
mov word [ebp], 5000h | 'v'
|
|
inc ebp
|
|
inc ebp
|
|
%endif
|
|
|
|
; now back to real mode
|
|
o32 lgdt [ MULTIBOOT_BASE+mbootgdtdesc ]
|
|
; set our GDT
|
|
mov ax, 10h
|
|
mov ds, ax
|
|
mov es, ax
|
|
mov ss, ax
|
|
mov fs, ax
|
|
mov gs, ax ; set 64 KiB segment limits
|
|
o32 push byte 0
|
|
o32 popf ; reset all flags
|
|
jmp 08h:.pm16 ; use 16-bit selector
|
|
|
|
[bits 16]
|
|
; now really switch to real mode
|
|
; (already executing relocated code)
|
|
.pm16:
|
|
mov eax, cr0
|
|
dec ax
|
|
mov cr0, eax ; clear PE bit
|
|
|
|
jmp dword MULTIBOOT_TARGET_SEGMENT:.rm ; reload cs
|
|
|
|
; Set up registers and the environment for
|
|
; the kernel entry point. It doesn't need initialised
|
|
; segment registers but we'll initialise them in case
|
|
; they still contain selector content.
|
|
.rm:
|
|
; some sources indicate we should set the IDT
|
|
o32 lidt [cs:mbootidtdesc] ; set IDT to RM IVT
|
|
xor ax, ax
|
|
mov ds, ax
|
|
mov es, ax
|
|
mov fs, ax
|
|
mov gs, ax
|
|
mov ss, ax
|
|
; (cs should be = 200h now, need no far jump)
|
|
; cs = 200h, ip = mboot_86m_entry
|
|
; ds = es = fs = gs = ss = 0
|
|
; sp = 2000h - 512 - 16 + lsvCommandLine.start
|
|
; dl = boot unit (or -1)
|
|
; dh = boot partition (0..3 = primary, 4+ = logical, or -1)
|
|
|
|
mov bp, MULTIBOOT_BPB ; -> pseudo BPB
|
|
mov di, MULTIBOOT_BPB + lsvCommandLine.signature
|
|
mov ax, lsvclSignature
|
|
stosw ; store signature
|
|
mov cx, (- (lsvCommandLine.signature + 2) + 512 + 3) / 4
|
|
xor eax, eax
|
|
rep stosd ; clear
|
|
; lsvFirstCluster = 0 (same as for FreeDOS entrypoint)
|
|
; bsBPB + bpbHiddenSectors = 0 (invalid or unpartitioned)
|
|
; bsBPB + bpbCHSSectors = 0
|
|
; bsBPB + bpbCHSHeads = 0
|
|
|
|
inc ax
|
|
mov dword [bp + lsvDataStart], eax
|
|
; lsvDataStart = 1 (placeholder)
|
|
__CPU__
|
|
mov word [bp + bsBPB + bpbNumRootDirEnts], ax
|
|
mov word [bp + bsBPB + bpbSectorsPerFAT], ax
|
|
; do not detect as FAT32
|
|
mov byte [bp + bsBPB + bpbSectorsPerCluster], al
|
|
mov word [bp + bsBPB + bpbBytesPerSector], 512
|
|
mov word [bp + bsBPB + bpbTotalSectors], 8192
|
|
; 1 C/s * 8 Ki sectors = 8 Ki clusters, detect as FAT16
|
|
; that is, do not load FAT at all (would load a FAT12)
|
|
|
|
mov byte [bp + bsBPB + bpbNew + bpbnBootUnit], dl
|
|
; set boot load unit
|
|
|
|
%if _LSVEXTRA
|
|
%if _MULTIBOOT_DEBUG
|
|
mov ax, dx
|
|
call mb_disp_ax_hex
|
|
%endif
|
|
xchg dl, dh ; dl = Multiboot spec partition number
|
|
; (0..3 primary, 4+ logical, -1 invalid)
|
|
inc dx ; 0 invalid, 1..4 primary, 5+ logical
|
|
mov dh, lsvefPartitionNumber
|
|
mov word [bp + lsvExtra], dx
|
|
%endif
|
|
jmp freedos_entry.multiboot_entry
|
|
|
|
|
|
%if _MULTIBOOT_DEBUG
|
|
mb_disp_ax_hex: ; ax
|
|
xchg al,ah
|
|
call mb_disp_al_hex ; display former ah
|
|
xchg al,ah ; and fall trough for al
|
|
mb_disp_al_hex: ; al
|
|
push cx
|
|
mov cl,4
|
|
ror al,cl
|
|
call mb_disp_al_lownibble_hex ; display former high-nibble
|
|
rol al,cl
|
|
pop cx
|
|
; and fall trough for low-nibble
|
|
mb_disp_al_lownibble_hex:
|
|
push ax ; save ax for call return
|
|
and al,00001111b ; high nibble must be zero
|
|
add al,'0' ; if number is 0-9, now it's the correct character
|
|
cmp al,'9'
|
|
jna .decimalnum ; if we get decimal number with this, ok -->
|
|
add al,7 ; otherwise, add 7 and we are inside our alphabet
|
|
.decimalnum:
|
|
call mb_disp_al
|
|
pop ax
|
|
retn
|
|
|
|
mb_disp_al:
|
|
push ax
|
|
push bx
|
|
push bp
|
|
mov ah, 0Eh
|
|
mov bx, 7
|
|
int 10h
|
|
pop bp
|
|
pop bx
|
|
pop ax
|
|
retn
|
|
%endif
|
|
|
|
|
|
align 16, db 0
|
|
mbootgdt:
|
|
dw 0,0
|
|
db 0,0,0,0
|
|
|
|
; selector 8: 16-bit real mode CS
|
|
; base = 00002000h, limit 0FFFFh (1 B Granularity), present
|
|
; type = 16-bit code execute/read only/conforming, DPL = 0
|
|
dw 0FFFFh,MULTIBOOT_TARGET
|
|
db 0,9Eh,0,0
|
|
|
|
; selector 10h: 16-bit real mode DS
|
|
; base = 00000000h, limit 0FFFFh (1 B Granularity), present
|
|
; type = 16-bit data read/write, DPL = 0
|
|
dw 0FFFFh,0
|
|
db 0,92h,0,0
|
|
endarea mbootgdt
|
|
|
|
mbootgdtdesc:
|
|
dw mbootgdt_size-1 ; limit
|
|
dd MULTIBOOT_BASE+mbootgdt ; address
|
|
|
|
mbootidtdesc:
|
|
dw 400h-1 ; limit
|
|
dd 0 ; address (86M IVT)
|