diff --git a/core/isolinux.asm b/core/isolinux.asm index 2627c2d..a0910fb 100644 --- a/core/isolinux.asm +++ b/core/isolinux.asm @@ -1135,73 +1135,23 @@ all_read: ; (which will be at 16 only for a single-session disk!); from the PVD ; we should be able to find the rest of what we need to know. ; -get_fs_structures: - mov eax,[bi_pvd] - mov bx,trackbuf - call getonesec - - mov eax,[trackbuf+156+2] - mov [RootDir+dir_lba],eax - mov [CurrentDir+dir_lba],eax -%ifdef DEBUG_MESSAGES - mov si,dbg_rootdir_msg - call writemsg - call writehex8 - call crlf -%endif - mov eax,[trackbuf+156+10] - mov [RootDir+dir_len],eax - mov [CurrentDir+dir_len],eax - add eax,SECTOR_SIZE-1 - shr eax,SECTOR_SHIFT - mov [RootDir+dir_clust],eax - mov [CurrentDir+dir_clust],eax - - ; Look for an isolinux directory, and if found, - ; make it the current directory instead of the root - ; directory. - ; Also copy the name of the directory to CurrentDirName - mov word [CurrentDirName],ROOT_DIR_WORD ; Write '/',0 to the CurrentDirName + call iso_mount mov di,boot_dir ; Search for /boot/isolinux - mov al,02h - push di - call searchdir_iso - pop di - jnz .found_dir - mov di,isolinux_dir - mov al,02h ; Search for /isolinux - push di - call searchdir_iso - pop di - jz .no_isolinux_dir + call setcwd + jnc .found_dir + mov di,isolinux_dir ; Search for /isolinux + call setcwd .found_dir: - ; Copy current directory name to CurrentDirName - push si - push di - mov si,di - mov di,CurrentDirName - call strcpy - mov byte [di],0 ;done in case it's not word aligned - dec di - mov byte [di],'/' - pop di - pop si - mov [CurrentDir+dir_len],eax - mov eax,[si+file_left] - mov [CurrentDir+dir_clust],eax - xor eax,eax ; Free this file pointer entry - xchg eax,[si+file_sector] - mov [CurrentDir+dir_lba],eax %ifdef DEBUG_MESSAGES push si mov si,dbg_isodir_msg call writemsg pop si + mov eax,[CurrentDir+dir_lba] call writehex8 call crlf %endif -.no_isolinux_dir: ; ; Locate the configuration file @@ -1706,6 +1656,90 @@ getfssec: TRACER 'f' ret +; +; setcwd: Set current working directory. +; +; On entry: +; DI -> directory name +; On exit: +; CF = 1 -> error +; +; On error, the old working directory is kept. +; +setcwd: + mov al,02h + push di + call searchdir_iso + pop di + stc + jz .err + mov [CurrentDir+dir_len],eax + mov eax,[si+file_left] + mov [CurrentDir+dir_clust],eax + xor eax,eax + xchg eax,[si+file_sector] + mov [CurrentDir+dir_lba],eax + mov si,di + mov di,CurrentDirName + cmp si,di + jz .ok + mov cx,FILENAME_MAX + push ds + pop es +.copy: + lodsb + stosb + or al,al + loopnz .copy + mov byte [di-1],0 + jnz .err +.ok: + clc +.err: + ret + +; +; Read fs meta data and setup RootDir and CurrentDir. +; +; On exit: +; CF = 1 -> error +; +iso_mount: + mov eax,[bi_pvd] + mov bx,trackbuf + call getonesec + + mov eax,[trackbuf+156+10] + mov [RootDir+dir_len],eax + add eax,SECTOR_SIZE-1 + shr eax,SECTOR_SHIFT + mov [RootDir+dir_clust],eax + mov eax,[trackbuf+156+2] + mov [RootDir+dir_lba],eax + + push ds + pop es + mov si,RootDir + mov di,CurrentDir + mov cx,dir_t_size + rep movsb + +%ifdef DEBUG_MESSAGES + mov si,dbg_rootdir_msg + call writemsg + call writehex8 + call crlf +%endif + + mov di,CurrentDirName + call setcwd + jnc .ok + mov word [CurrentDirName],ROOT_DIR_WORD +.ok: + clc + ret + + ; ----------------------------------------------------------------------------- ; Common modules ; ----------------------------------------------------------------------------- diff --git a/core/abort.inc b/core/abort.inc index 5b16b9d..cc59fa7 100644 --- a/core/abort.inc +++ b/core/abort.inc @@ -24,6 +24,10 @@ ; assumes CS == DS ; dot_pause: + push ax + mov al,5 + call [comboot_hook] + pop ax push si mov si,dot_msg call writestr_qchk @@ -63,6 +67,8 @@ abort_check: abort_load: mov bx,error_or_command abort_load_chain: + mov al,80h + call [comboot_hook] ; may not return RESET_STACK_AND_SEGS AX call writestr ; Expects SI -> error msg diff --git a/core/loadhigh.inc b/core/loadhigh.inc index 8ff9da1..91061fc 100644 --- a/core/loadhigh.inc +++ b/core/loadhigh.inc @@ -101,6 +101,8 @@ load_high: ret .overflow: mov si,err_nohighmem + mov al,83h + call [comboot_hook] ; may not return jmp abort_load section .data diff --git a/core/runkernel.inc b/core/runkernel.inc index 8bfc8b8..f458fc7 100644 --- a/core/runkernel.inc +++ b/core/runkernel.inc @@ -228,6 +228,8 @@ new_kernel: mov [LoadFlags],al any_kernel: + mov al,4 + call [comboot_hook] mov si,loading_msg call writestr_qchk mov si,KernelCName ; Print kernel name part of @@ -319,6 +321,9 @@ load_initrd: ; call abort_check ; Last chance!! + mov al,6 + call [comboot_hook] + mov si,ready_msg call writestr_qchk @@ -489,6 +494,8 @@ old_kernel: xor ax,ax cmp word [InitRDPtr],ax ; Old kernel can't have initrd je .load + mov al,82h + call [comboot_hook] ; may not return mov si,err_oldkernel jmp abort_load .load: @@ -613,6 +620,8 @@ loadinitrd: ret .notthere: + mov al,81h + call [comboot_hook] ; may not return mov si,err_noinitrd call writestr mov si,InitRDCName diff --git a/core/ui.inc b/core/ui.inc index 1b40717..9413f16 100644 --- a/core/ui.inc +++ b/core/ui.inc @@ -402,8 +402,12 @@ vk_check: %if HAS_LOCALBOOT ; Is this a "localboot" pseudo-kernel? cmp al,VK_LOCALBOOT ; al == KernelType + jne .no_local_boot + mov al,7 + call [comboot_hook] mov ax,[VKernelBuf+vk_rname] ; Possible localboot type - je local_boot + jmp local_boot +.no_local_boot: %endif jmp get_kernel @@ -468,6 +472,8 @@ bad_kernel: .really: mov si,KernelName mov di,KernelCName + mov al,81h + call [comboot_hook] ; may not return push di call unmangle_name ; Get human form mov si,err_notfound ; Complain about missing kernel @@ -510,7 +516,10 @@ on_error: ; ; kernel_corrupt: Called if the kernel file does not seem healthy ; -kernel_corrupt: mov si,err_notkernel +kernel_corrupt: + mov al,82h + call [comboot_hook] ; may not return + mov si,err_notkernel jmp abort_load ; diff --git a/core/comboot.inc b/core/comboot.inc index cdba16d..1a1dbfe 100644 --- a/core/comboot.inc +++ b/core/comboot.inc @@ -96,24 +96,23 @@ is_comboot_image: shl ax,6 ; Kilobytes -> paragraphs mov word [es:02h],ax -%ifndef DEPEND -%if real_mode_seg != comboot_seg -%error "This code assumes real_mode_seg == comboot_seg" -%endif -%endif ; Copy the command line from high memory + push word real_mode_seg + pop ds mov si,cmd_line_here mov cx,125 ; Max cmdline len (minus space and CR) mov di,081h ; Offset in PSP for command line mov al,' ' ; DOS command lines begin with a space stosb -.loop: es lodsb +.loop: lodsb and al,al jz .done stosb loop .loop .done: + push cs + pop ds mov al,0Dh ; CR after last character stosb diff --git a/core/layout.inc b/core/layout.inc index 8c2e248..ca95d2b 100644 --- a/core/layout.inc +++ b/core/layout.inc @@ -123,4 +123,4 @@ real_mode_seg equ cache_seg + 1000h pktbuf_seg equ cache_seg ; PXELINUX packet buffers %endif -comboot_seg equ real_mode_seg ; COMBOOT image loading zone +comboot_seg equ real_mode_seg + 1000h ; COMBOOT image loading zone diff --git a/core/runkernel.inc b/core/runkernel.inc index f458fc7..f6ed644 100644 --- a/core/runkernel.inc +++ b/core/runkernel.inc @@ -165,7 +165,7 @@ opt_mem: ret opt_quiet: - mov byte [QuietBoot],1 + or byte [QuietBoot],1 ret %if IS_PXELINUX @@ -634,7 +634,7 @@ loadinitrd: ; assumes CS == DS ; writestr_qchk: - test byte [QuietBoot],01h + test byte [QuietBoot],03h jz writestr ret @@ -689,4 +689,6 @@ KernelVersion resw 1 ; Kernel protocol version ; InitRDPtr resw 1 ; Pointer to initrd= option in command line LoadFlags resb 1 ; Loadflags from kernel -QuietBoot resb 1 ; Set if a quiet boot is requested + + section .data +QuietBoot db 0 ; Set if a quiet boot is requested diff --git a/core/ui.inc b/core/ui.inc index 9413f16..353d59a 100644 --- a/core/ui.inc +++ b/core/ui.inc @@ -600,7 +600,7 @@ kernel_good: ; xor ax,ax mov [InitRDPtr],ax - mov [QuietBoot],al + and byte [QuietBoot],~1 %if IS_PXELINUX mov [KeepPXE],al %endif diff --git a/core/runkernel.inc b/core/runkernel.inc index f6ed644..286c9c8 100644 --- a/core/runkernel.inc +++ b/core/runkernel.inc @@ -259,7 +259,7 @@ read_kernel: mov ecx,8000h ; 32K sub ecx,esi ; Number of bytes to copy add esi,(real_mode_seg << 4) ; Pointer to source - mov edi,100000h ; Copy to address 100000h + mov edi,[KernelStart] ; Copy to kernel address call bcopy ; Transfer to high memory @@ -431,7 +431,7 @@ setup_move: mov eax,10000h ; Target address of low kernel stosd - mov eax,100000h ; Where currently loaded + mov eax,[KernelStart] ; Where currently loaded stosd neg eax add eax,[KernelEnd] @@ -439,9 +439,13 @@ setup_move: inc cx mov bx,9000h ; Revised real mode segment + jmp .loading_initrd .loading_high: + mov eax,[KernelStart] + mov [fs:su_code32start],eax +.loading_initrd: cmp word [InitRDPtr],0 ; Did we have an initrd? je .no_initrd @@ -692,3 +696,5 @@ LoadFlags resb 1 ; Loadflags from kernel section .data QuietBoot db 0 ; Set if a quiet boot is requested + alignz 4 +KernelStart dd 100000h diff --git a/core/comboot.inc b/core/comboot.inc index 1a1dbfe..1923308 100644 --- a/core/comboot.inc +++ b/core/comboot.inc @@ -962,6 +962,45 @@ comapi_shufraw: mov ecx,P_ECX jmp shuffle_and_boot_raw + +; +; INT 22h AX=0025h Set current working directory +; +%if IS_ISOLINUX +comapi_setcwd: + mov si,P_BX + mov di,TmpDirName + mov cx,FILENAME_MAX + mov ds,P_ES +.copy: + lodsb + stosb + or al,al + loopnz .copy + push cs + pop ds + stc + jnz .err + mov di,TmpDirName + call setcwd +.err: + ret +%else +comapi_setcwd equ comapi_err +%endif + + +; +; INT 22h AX=0026h Read filesystem meta data +; +%if IS_ISOLINUX +comapi_mount: +; call iso_mount + ret +%else +comapi_mount equ comapi_err +%endif + section .data %macro int21 2 @@ -969,6 +1008,109 @@ comapi_shufraw: dw %2 %endmacro + +; +; INT 22h AX=0027h Run command, return on error +; +; Terminates the COMBOOT program and executes the command line in +; ES:BX as if it had been entered by the user. +; CS:SI: comboot callback (via far call) +; EDI kernel load address +; EDX memory end (sets MyHighMemSize if != 0) +; +comapi_run2: + push word P_CS + push word P_SI + pop dword [comboot_far] + push dword P_EDI + pop dword [KernelStart] + mov edx,P_EDX + or edx,edx + jz .nohimemsize +%if HIGHMEM_SLOP != 0 + sub edx,HIGHMEM_SLOP +%endif +.nohimemsize: + mov [AltHighMemSize],edx + mov ds,P_ES + mov si,P_BX + mov di,command_line + call strcpy + push cs + pop ds + push cs + pop es + mov [comboot_sp_save],sp ; save stack pointer + mov word [comboot_hook],comboot_hook_entry + or byte [QuietBoot],2 + jmp load_kernel ; Run a new kernel + +comapi_run2_cont: + mov word [comboot_hook],comboot_hook_nop + mov sp,[comboot_sp_save] ; fix stack pointer + and byte [QuietBoot],~2 + clc + ret + + +; +; INT 22h AX=0028h Get memory size +; +comapi_memsize: + push dword [HighMemSize] + pop dword P_EAX + clc + ret + + +; +; Callback function used at various places during kernel/initrd loading. +; +; The function either returns or continues at comapi_run2_cont. +; +; AL: +; bit 7: 0/1 return/don't return +; bit 0-6: function code +; 0: abort kernel/initrd loading +; 1: kernel/initrd not found +; 2: kernel corrupt +; 3: out of memory (while initrd loading) +; 4: progress start +; 5: progress increment +; 6: progress end: kernel loaded, stop gfxboot +; 7: stop gfxboot +; +comboot_hook_entry: + pushad + push gs + push fs + push es + push ds + call far [comboot_far] + pop ds + pop es + pop fs + pop gs + popad + pushad + and al,7fh + cmp al,6 + jnz .notlast + push es + mov si,DOSSaveVectors + mov di,4*20h + mov cx,20h + push word 0 + pop es + rep movsd ; Restore DOS-range vectors + pop es +.notlast: + popad + test al,80h + jnz comapi_run2_cont +comboot_hook_nop: + ret + int21_table: int21 00h, comboot_return int21 01h, comboot_getkey @@ -1021,8 +1163,16 @@ int22_table: dw comapi_closedir ; 0022 close directory dw comapi_shufsize ; 0023 query shuffler size dw comapi_shufraw ; 0024 cleanup, shuffle and boot raw + dw comapi_setcwd ; 0025 set current working directory + dw comapi_mount ; 0026 read fs structures (aka mount) + dw comapi_run2 ; 0027 like 0003, but return on error + dw comapi_memsize ; 0028 get memory size int22_count equ ($-int22_table)/2 +comboot_sp_save dw 0 +comboot_hook dw comboot_hook_nop +comboot_far dd 0 + APIKeyWait db 0 APIKeyFlag db 0 @@ -1041,8 +1191,10 @@ feature_flags_len equ ($-feature_flags) err_notdos db ': attempted DOS system call INT ',0 err_comlarge db 'COMBOOT image too large.', CR, LF, 0 - section .bss1 + section .bss2 alignb 4 +AltHighMemSize resd 1 DOSErrTramp resd 33 ; Error trampolines +TmpDirName resb FILENAME_MAX ConfigName resb FILENAME_MAX CurrentDirName resb FILENAME_MAX diff --git a/core/comboot.inc b/core/comboot.inc index 1923308..f39bfb1 100644 --- a/core/comboot.inc +++ b/core/comboot.inc @@ -1043,12 +1043,14 @@ comapi_run2: mov [comboot_sp_save],sp ; save stack pointer mov word [comboot_hook],comboot_hook_entry or byte [QuietBoot],2 + mov byte [comboot_run2_active],1 jmp load_kernel ; Run a new kernel comapi_run2_cont: mov word [comboot_hook],comboot_hook_nop mov sp,[comboot_sp_save] ; fix stack pointer and byte [QuietBoot],~2 + mov byte [comboot_run2_active],0 clc ret @@ -1172,6 +1174,7 @@ int22_count equ ($-int22_table)/2 comboot_sp_save dw 0 comboot_hook dw comboot_hook_nop comboot_far dd 0 +comboot_run2_active db 0 APIKeyWait db 0 APIKeyFlag db 0 diff --git a/core/ui.inc b/core/ui.inc index 353d59a..e37f2a7 100644 --- a/core/ui.inc +++ b/core/ui.inc @@ -379,9 +379,13 @@ vk_check: push word real_mode_seg pop es mov di,cmd_line_here + ; append line already included in this case + cmp byte [comboot_run2_active],0 + jnz .no_append_copy mov si,VKernelBuf+vk_append mov cx,[VKernelBuf+vk_appendlen] rep movsb +.no_append_copy: mov [CmdLinePtr],di ; Where to add rest of cmd pop es mov di,KernelName diff --git a/core/comboot.inc b/core/comboot.inc index f39bfb1..0874526 100644 --- a/core/comboot.inc +++ b/core/comboot.inc @@ -1088,6 +1088,7 @@ comboot_hook_entry: push fs push es push ds + mov ecx,[KernelSize] call far [comboot_far] pop ds pop es diff --git a/core/ui.inc b/core/ui.inc index e37f2a7..6137dc9 100644 --- a/core/ui.inc +++ b/core/ui.inc @@ -616,7 +616,11 @@ kernel_good: mov [KernelCNameLen],di ; Default memory limit, can be overridden by image loaders + mov eax,[AltHighMemSize] + or eax,eax + jnz .altsize mov eax,[HighMemRsvd] +.altsize: mov [MyHighMemSize],eax popad @@ -637,6 +641,7 @@ kernel_good: ; At this point, EAX contains the size of the kernel, SI contains ; the file handle/cluster pointer, and ECX contains the extension (if any.) ; + mov [KernelSize],eax movzx di,byte [KernelType] add di,di jmp [kerneltype_table+di] diff --git a/doc/comboot.txt b/doc/comboot.txt index f5fefda..1450021 100644 --- a/doc/comboot.txt +++ b/doc/comboot.txt @@ -955,3 +955,38 @@ AX=0024h [3.80] Cleanup, shuffle and boot, raw version with read/write data segments, matching the respective code segment. For mode 0, B=0 and the limits will be 64K, for mode 1, B=1 and the limits will be 4 GB. + + +AX=0025h [3.84] Set current working directory + Input: AX 00025h + ES:BX null-terminated directory name string + Output: None + + Sets the current working directory. For SYSLINUX, ISOLINUX, + and PXELINUX, this will be an absolute path. + + +AX=0026h [3.84] Read file system metadata [ISOLINUX] + Input: AX 00026h + Output: None + + Reads filesystem data (e.g. after a CDROM change). + + +AX=0027h [3.84] Run command + Input: AX 0027h + ES:BX null-terminated command string + SI comboot callback function (called via far call) + EDI kernel load address + EDX if != 0: initrd load address (that is: memory end) + Output: Does not return if the kernel loads correctly. + + Executes the command line as if it had been entered by the user. + Note that this function does return (with CF = 0) if there are + problems or the user aborted the load. Else it terminates the + COMBOOT program and starts the kernel. + +AX=0028h [3.84] Get memory size + Input: AX 0028h + Output: EAX high memory size (in bytes) + diff --git a/modules/Makefile b/modules/Makefile index 77020ea..e6e7905 100644 --- a/modules/Makefile +++ b/modules/Makefile @@ -19,6 +19,11 @@ include $(topdir)/MCONFIG.embedded INCLUDES = -I$(com32)/include +CFLAGS_COM = -O2 -Wall -Wno-pointer-sign -fomit-frame-pointer -m32 -march=i386 \ + -fno-align-functions -fno-align-labels -fno-align-jumps -fno-align-loops \ + -fno-builtin -nostdinc -I . +ASMFLAGS_COM = -O99 -felf + BINS = pxechain.com gfxboot.com poweroff.com int18.com all: $(BINS) @@ -49,6 +54,15 @@ $(LIB): $(LIBOBJS) %.ppm.gz: %.png $(PNGTOPNM) $< | gzip -9 > $@ +libio.o: libio.asm + nasm $(ASMFLAGS_COM) -o $@ -l $*.lst $< + +gfxboot.o: gfxboot.c libio.h + $(CC) -g $(CFLAGS_COM) -c -o $@ $< + +gfxboot.com: gfxboot.o libio.o + ld -M -Tcom.ld -o $@ $^ >$*.map + tidy dist: rm -f *.o *.a *.lst *.elf *.map .*.d diff --git a/modules/com.ld b/modules/com.ld new file mode 100644 index 0000000..a98f9aa --- /dev/null +++ b/modules/com.ld @@ -0,0 +1,16 @@ +/* + linker script for DOS program (.COM) + */ + +OUTPUT_FORMAT("binary") +OUTPUT_ARCH(i386) +SEARCH_DIR(".") +ENTRY(_start) +SECTIONS +{ + .init 0x100 : { *(.init) } + .text : { *(.text) } + .rodata : { *(.rodata*) } + .data : { *(.data) } + .bss : { __bss_start = .; *(.bss) } +} diff --git a/modules/gfxboot.c b/modules/gfxboot.c new file mode 100644 index 0000000..a59da40 --- /dev/null +++ b/modules/gfxboot.c @@ -0,0 +1,1040 @@ +/* + * + * gfxboot.c + * + * A comboot program to load gfxboot graphics. + * + * It is based on work done by Sebastian Herbszt in gfxboot.asm. + * + * Copyright (c) 2009 Steffen Winterfeldt. + * + * 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, Inc., 53 Temple Place Ste 330, Boston MA + * 02111-1307, USA; either version 2 of the License, or (at your option) any + * later version; incorporated herein by reference. + * + */ + +#include + +#define ID_SYSLINUX 0x31 +#define ID_PXELINUX 0x32 +#define ID_ISOLINUX 0x33 +#define ID_EXTLINUX 0x34 + +#define MAX_CONFIG_LINE_LEN 2048 +#define MAX_CMDLINE_LEN 1024 + +// basic memory layout in MB +#define GFX_MEMORY_START 1 +#define GFX_MEMORY_SIZE 7 +// assume initrd needs at least that much +#define INITRD_MIN_MEMORY 64 + +#define GFX_BC_INIT 0 +#define GFX_BC_DONE 1 +#define GFX_BC_INPUT 2 +#define GFX_BC_MENU_INIT 3 +#define GFX_BC_INFOBOX_INIT 4 +#define GFX_BC_INFOBOX_DONE 5 +#define GFX_BC_PROGRESS_INIT 6 +#define GFX_BC_PROGRESS_DONE 7 +#define GFX_BC_PROGRESS_UPDATE 8 +#define GFX_BC_PROGRESS_LIMIT 9 // unused +#define GFX_BC_PASSWORD_INIT 10 +#define GFX_BC_PASSWORD_DONE 11 + +// for now, static values +#define MENU_LABEL_SIZE 128 +#define MENU_ARG_SIZE 512 +#define MENU_ENTRY_SIZE (MENU_LABEL_SIZE + MENU_ARG_SIZE) +// entry 0 is reserved for the default entry +#define MAX_MENU_ENTRIES (0x10000 / MENU_ENTRY_SIZE) + + +typedef struct { + uint16_t handle; + uint16_t block_size; + int file_size; // file size (-1: unknown) + uint32_t buf; // must be block_size + unsigned buf_size; // in block_size units + unsigned data_len; // valid bytes in buf + unsigned buf_idx; // read pos in buffer +} fd_t; + + +// gfx config data (52 bytes) +typedef struct __attribute__ ((packed)) { + uint8_t bootloader; // 0: boot loader type (0: lilo, 1: syslinux, 2: grub) + uint8_t sector_shift; // 1: sector shift + uint8_t media_type; // 2: media type (0: disk, 1: floppy, 2: cdrom) + uint8_t failsafe; // 3: turn on failsafe mode (bitmask) + // 0: SHIFT pressed + // 1: skip gfxboot + // 2: skip monitor detection + uint8_t sysconfig_size; // 4: size of sysconfig data + uint8_t boot_drive; // 5: BIOS boot drive + uint16_t callback; // 6: offset to callback handler + uint16_t bootloader_seg; // 8: code/data segment used by bootloader; must follow gfx_callback + uint16_t reserved_1; // 10 + uint32_t user_info_0; // 12: data for info box + uint32_t user_info_1; // 16: data for info box + uint32_t bios_mem_size; // 20: BIOS memory size (in bytes) + uint16_t xmem_0; // 24: extended mem area 0 (start:size in MB; 12:4 bits) + uint16_t xmem_1; // 26: extended mem area 1 + uint16_t xmem_2; // 28: extended mem area 2 + uint16_t xmem_3; // 30: extended mem area 3 + uint32_t file; // 32: start of gfx file + uint32_t archive_start; // 36: start of cpio archive + uint32_t archive_end; // 40: end of cpio archive + uint32_t mem0_start; // 44: low free memory start + uint32_t mem0_end; // 48: low free memory end +} gfx_config_t; + + +// menu description (18 bytes) +typedef struct __attribute__ ((packed)) { + uint16_t entries; + uint32_t default_entry; // seg:ofs + uint32_t label_list; // seg:ofs + uint16_t label_size; + uint32_t arg_list; // seg:ofs + uint16_t arg_size; +} menu_t; + + +// e820 mem descriptor +typedef struct __attribute__ ((packed)) { + uint64_t base; + uint64_t len; + uint32_t type; + uint32_t cont; +} mem_info_t; + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// global file descriptor, implicitly used by read(), getc(), fgets() +fd_t fd; + +gfx_config_t gfx_config; +menu_t menu; + +struct { + uint32_t jmp_table[12]; + uint16_t code_seg; + char fname_buf[64]; +} gfx; + +unsigned comboot, comboot_len; +unsigned io_buf, io_buf_len; +unsigned menu_buf, menu_buf_len; +unsigned freemem, freemem_len; + +unsigned initrd_end; +unsigned kernel_start; + +int timeout; + +char cmdline[MAX_CMDLINE_LEN]; +char current_label[64]; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +int open(char *name); +int open32(uint32_t name); +int read(void *buf, int size); +int read32(uint32_t buf, int size); +int getc(void); +char *fgets(char *s, int size); + +int strlen(char *s); +int strcmp(char *s1, char *s2); +char *skip_spaces(char *s); +char *skip_nonspaces(char *s); +void chop_line(char *s); + +int atoi(char *s); + +uint32_t get_config_file_name32(void); +int read_config_file(void); + +unsigned magic_ok(uint32_t buf); +unsigned find_file(uint32_t buf, unsigned len, unsigned *gfx_file_start, unsigned *file_len); + +int get_mem_info(mem_info_t *mi); +int gfx_init(char *file); +void gfx_done(void); +int gfx_input(void); +int gfx_menu_init(void); + +void gfx_infobox32(int type, uint32_t str1, uint32_t str2); +void gfx_infobox(int type, char *str1, char *str2); + +void boot(void); +void show_message(char *file); + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int main(int argc, char **argv) +{ + x86regs_t r; + uint8_t syslinux_id; + int menu_index; + + r.eax = 0x0a; // Get Derivative-Specific Information + r.ecx = 9; + x86int(0x22, &r); + syslinux_id = (uint8_t) r.eax; + gfx_config.sector_shift = (uint8_t) r.ecx; + gfx_config.boot_drive = (uint8_t) r.edx; + + // define our memory layout + // all must be at least 16 bytes aligned + + // 64k comboot code + comboot = comboot_seg() << 4; + comboot_len = 0x10000; + + // 16k file io buffer + io_buf = comboot + comboot_len; + io_buf_len = 0x4000; + + // 64k for parsed menu data + menu_buf = io_buf + io_buf_len; + menu_buf_len = 0x10000; + + // use remaining mem for gfx core + freemem = menu_buf + menu_buf_len; + // comboot api passes low memory end at address 2 + freemem_len = ((*(uint16_t *) 2) << 4) - freemem; + + gfx_config.bootloader = 1; + gfx_config.sysconfig_size = sizeof gfx_config; + gfx_config.bootloader_seg = comboot >> 4; + + // not gfx_cb() directly, we need a wrapper + gfx_config.callback = (uint32_t) _gfx_cb; + + if(syslinux_id == ID_PXELINUX) { + gfx_config.sector_shift = 11; + gfx_config.boot_drive = 0; + } + + if(argc < 2) { + printf("Usage: gfxboot.com bootlogo_file [message_file]\n"); + if(argc > 2) show_message(argv[2]); + + return 0; + } + + if(read_config_file()) { + printf("Error reading config file\n"); + if(argc > 2) show_message(argv[2]); + + return 0; + } + + if(gfx_init(argv[1])) { + printf("Error setting up gfxboot\n"); + if(argc > 2) show_message(argv[2]); + + return 0; + } + + gfx_menu_init(); + + for(;;) { + menu_index = gfx_input(); + + // abort gfx, return to text mode prompt + if(menu_index == -1) { + gfx_done(); + break; + } + + // get label name, is needed later for messages etc. + memcpy(current_label, cmdline, sizeof current_label); + current_label[sizeof current_label - 1] = 0; + *skip_nonspaces(current_label) = 0; + + // does not return if it succeeds + boot(); + } + + if(argc > 2) show_message(argv[2]); + + return 0; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// +// return: +// 0: ok, -1: error +// +int open32(uint32_t name) +{ + x86regs_t r; + + fd.handle = 0; + fd.data_len = fd.buf_idx = 0; + + r.esi = name & 0xf; + r.eax = 0x06; // Open file + r.es = name >> 4; + x86int(0x22, &r); + + fd.block_size = r.ecx; + fd.file_size = r.eax; + + if( + (r.eflags & X86_CF) || + !fd.file_size || !fd.block_size || fd.block_size > io_buf_len + ) return -1; + + fd.handle = r.esi; + + fd.buf = io_buf; + fd.buf_size = io_buf_len / fd.block_size; + + // printf("block size = 0x%x, file size = %d\n", fd.block_size, fd.file_size); + + return 0; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// +// return: +// 0: ok, -1: error +// +int open(char *name) +{ + return open32((uint32_t) name + comboot); +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// +// Note: buf is 32bit address +// +int read32(uint32_t buf, int size) +{ + x86regs_t r; + int i, len = 0; + + while(size) { + i = fd.data_len - fd.buf_idx; + + if(i > 0) { + i = i < size ? i : size; + memcpy32(buf, fd.buf + fd.buf_idx, i); + len += i; + buf += i; + size -= i; + fd.buf_idx += i; + } + + if(!size || !fd.handle) break; + + r.eax = 0x07; // Read file + r.ecx = fd.buf_size; + r.ebx = 0; + r.es = fd.buf >> 4; + r.esi = fd.handle; + x86int(0x22, &r); + fd.handle = r.esi; + fd.data_len = r.ecx; + fd.buf_idx = 0; + } + + return len; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int read(void *buf, int size) +{ + return read32((uint32_t) buf + comboot, size); +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int getc() +{ + char buf[1]; + + return read(buf, 1) ? *buf : EOF; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +char *fgets(char *s, int size) +{ + char *buf = s; + int c = EOF; + + while(size--) { + c = getc(); + if(c == EOF) break; + *buf++ = c; + if(c == 0x0a) break; + } + + *buf = 0; + + return c == EOF && s == buf ? 0 : s; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int strlen(char *s) +{ + char *s0 = s + 1; + + while(*s++); + + return s - s0; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int strcmp(char *s1, char *s2) +{ + while(*s1 && *s1 == *s2) s1++, s2++; + + return (uint8_t) *s1 - (uint8_t) *s2; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +char *skip_spaces(char *s) +{ + while(*s && (*s == ' ' || *s == '\t')) s++; + + return s; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +char *skip_nonspaces(char *s) +{ + while(*s && *s != ' ' && *s != '\t') s++; + + return s; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void chop_line(char *s) +{ + int i = strlen(s); + + if(!i) return; + + while(--i >= 0) { + if(s[i] == ' ' || s[i] == '\t' || s[i] == '\n') { + s[i] = 0; + } + else { + break; + } + } +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int atoi(char *s) +{ + int i = 0, sign = 1; + + if(*s == '-') s++, sign = -1; + if(*s == '+') s++; + + while(*s >= '0' && *s <= '9') { + i = 10 * i + *s++ - '0'; + } + + return sign * i; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// +// return: +// config file name (32 bit address) +// +uint32_t get_config_file_name32() +{ + x86regs_t r; + + r.eax = 0x0e; // Get configuration file name + x86int(0x22, &r); + + return (r.es << 4) + (uint16_t) r.ebx; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Read and parse syslinux config file. +// +// return: +// 0: ok, 1: error +// +int read_config_file(void) +{ + char *s, *t, buf[MAX_CONFIG_LINE_LEN]; + unsigned u; + int menu_idx = 0; + + // clear memory before we start + memset32(menu_buf, 0, menu_buf_len); + + if(open32(get_config_file_name32()) == -1) return 1; + + while((s = fgets(buf, sizeof buf))) { + chop_line(s); + s = skip_spaces(s); + if(!*s || *s == '#') continue; + t = skip_nonspaces(s); + if(*t) *t++ = 0; + t = skip_spaces(t); + + if(!strcmp(s, "timeout")) { + timeout = atoi(t); + } + + if(!strcmp(s, "default")) { + u = strlen(t); + if(u > MENU_LABEL_SIZE - 1) u = MENU_LABEL_SIZE - 1; + memcpy32(menu_buf, comboot + (uint32_t) t, u); + } + + if(!strcmp(s, "label")) { + menu_idx++; + if(menu_idx < MAX_MENU_ENTRIES) { + u = strlen(t); + if(u > MENU_LABEL_SIZE - 1) u = MENU_LABEL_SIZE - 1; + memcpy32( + menu_buf + menu_idx * MENU_LABEL_SIZE, + comboot + (uint32_t) t, + u + ); + } + } + + if(!strcmp(s, "append")) { + if(menu_idx < MAX_MENU_ENTRIES) { + u = strlen(t); + if(u > MENU_ARG_SIZE - 1) u = MENU_ARG_SIZE - 1; + memcpy32( + menu_buf + menu_idx * MENU_ARG_SIZE + MAX_MENU_ENTRIES * MENU_LABEL_SIZE, + comboot + (uint32_t) t, + u + ); + } + } + } + + menu.entries = menu_idx; + menu.label_size = MENU_LABEL_SIZE; + menu.arg_size = MENU_ARG_SIZE; + menu.default_entry = ((menu_buf >> 4) << 16) + (menu_buf & 0xf); + u = menu_buf + MENU_LABEL_SIZE; + menu.label_list = ((u >> 4) << 16) + (u & 0xf); + u = menu_buf + MAX_MENU_ENTRIES * MENU_LABEL_SIZE + MENU_ARG_SIZE; + menu.arg_list = ((u >> 4) << 16) + (u & 0xf); + + return 0; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Check header and return code start offset. +// +// Note: buf is 32bit address +// +unsigned magic_ok(uint32_t buf) +{ + if( + _mem32(buf) == 0x0b2d97f00 && /* magic id */ + (_mem8(buf + 4) == 8) /* version 8 */ + ) { + return _mem32(buf + 8); + } + + return 0; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// +// Search cpio archive for gfx file. +// +// Note: buf is 32bit address +// +unsigned find_file(uint32_t buf, unsigned len, unsigned *gfx_file_start, unsigned *file_len) +{ + unsigned i, fname_len, code_start = 0; + + *gfx_file_start = 0; + + for(i = 0; i < len;) { + if((len - i) >= 0x1a && _mem16(buf + i) == 0x71c7) { + fname_len = _mem16(buf + i + 20); + *file_len = _mem16(buf + i + 24) + (_mem16(buf + i + 22) << 16); + i += 26 + fname_len; + i = ((i + 1) & ~1); + if((code_start = magic_ok(buf + i))) { + *gfx_file_start = i; + return code_start; + } + i += *file_len; + i = ((i + 1) & ~1); + } + else { + break; + } + } + + return code_start; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void gfx_cb(x86regs_t *r) +{ + uint8_t f_nr = r->eax; + + switch(f_nr) { + case 0: // cb_status + // edx = filename buffer (64 bytes) + r->edx = comboot + (uint32_t) gfx.fname_buf; + r->eax = 0; + // printf("<0x%x, %p + 0x%x>", r->edx, gfx.fname_buf, comboot); + break; + + case 1: // cb_fopen + // file name in gfx.fname_buf + // al = 0: ok, 1: file not found + // ecx = file length (al = 0) + if(open(gfx.fname_buf) == -1) { + r->eax = 1; + } + else { + r->eax = 0; + r->ecx = fd.file_size; + } + break; + + case 2: // cb_fread + // edx = buffer address (linear) + // ecx = data length (< 64k) + if(!fd.handle) { + r->eax = r->ecx = 0; + break; + } + r->esi = fd.handle; + r->ebx = 0; + r->es = io_buf >> 4; + r->ecx = io_buf_len / fd.block_size; + r->eax = 7; + x86int(0x22, r); + fd.handle = r->esi; + if((r->eflags & X86_CF)) { + r->eax = 1; + } + else { + r->edx = io_buf; + r->eax = 0; + } + break; + + case 3: // cb_getcwd + // edx filename + r->eax = 0x1f; // Get current working directory + x86int(0x22, r); + r->edx = (r->es << 4) + (uint16_t) r->ebx; + r->eax = 0; + break; + + case 4: // cb_chdir + r->es = comboot >> 4; + r->ebx = (uint32_t) gfx.fname_buf; + r->eax = 0x25; // Get current working directory + x86int(0x22, r); + r->eax = (r->eflags & X86_CF) ? 1 : 0; + break; + + case 5: // cb_readsector + // in: edx = sector + // out: edx = buffer (linear address) + r->esi = r->edi = 0; + r->ebx = 0; + r->es = io_buf >> 4; + r->ecx = 1; + r->eax = 0x19; // Read disk + x86int(0x22, r); + r->eax = 0; + r->edx = io_buf; + break; + + case 6: // cb_mount + r->eax = 0x26; + x86int(0x22, r); + r->eax = (r->eflags & X86_CF) ? 1 : 0; + break; + + default: + r->eax = 0xff; + } +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// +// return: +// 0: ok, 1: error +// +int get_mem_info(mem_info_t *mi) +{ + x86regs_t r; + + mi->base = mi->len = mi->type = 0; + + r.eax = 0xe820; + r.edx = 0x534d4150; + r.ebx = mi->cont; + r.ecx = 20; + r.es = comboot >> 4; + r.edi = (unsigned) mi; + x86int(0x15, &r); + + mi->cont = 0; + if(r.eax != 0x534d4150 || (r.eflags & X86_CF)) return 1; + mi->cont = r.ebx; + + return 0; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// +// return: +// 0: ok, 1: error +// +int gfx_init(char *file) +{ + x86regs_t r; + int file_max, file_size, ofs; + unsigned u, code_start, file_start = 0, file_len = 0; + unsigned start2, len2, end2; + mem_info_t mi; + + gfx_config.mem0_start = freemem; + gfx_config.mem0_end = freemem + freemem_len; + + kernel_start = (GFX_MEMORY_START + GFX_MEMORY_SIZE) << 20; + initrd_end = 0; + + gfx_config.xmem_0 = (GFX_MEMORY_START << 4) + GFX_MEMORY_SIZE; + + r.eax = 0x28; // Get memory size + x86int(0x22, &r); + u = (r.eflags & X86_CF) ? 0 : r.eax; + + // round up + gfx_config.bios_mem_size = u ? ((u + 0xfffff) >> 20) << 20 : 256; + + if(u > 0) { + // new interface + + if(u < INITRD_MIN_MEMORY << 20) { + // ok, maybe there is a bigger block... + + mi.cont = 0; + start2 = len2 = 0; + while(!get_mem_info(&mi)) { +#if 0 + printf( + "%08x%08x %08x%08x %08x %08x\n", + (unsigned) (mi.base >> 32), (unsigned) mi.base, + (unsigned) (mi.len >> 32), (unsigned) mi.len, + mi.type, mi.cont + ); +#endif + if(mi.type == 1) { + if((mi.base >> 32) || (mi.base + mi.len) >> 32) break; + if(mi.len > len2) { + start2 = mi.base; + len2 = mi.len; + } + } + + if(!mi.cont) break; + } + +#if 0 + printf("largest: %08x %08x\n", start2, len2); + getchar(); +#endif + + if(len2 && len2 > 2 << 20 && len2 > u) { + // align to full MBs + end2 = (start2 + len2) & ~0xfffff; + start2 = (start2 + 0xfffff) & ~0xfffff; + len2 = end2 - start2; + } + else { + start2 = len2 = 0; + } + + if(len2) { + u = len2; + initrd_end = end2; + // we could relocate the kernel as well... + // kernel_start = start2; + } + } + + if(u < INITRD_MIN_MEMORY << 20) { + // a bit too small for us + printf("%u MB RAM is a bit small... - giving up\n", u >> 20); + + return 1; + } + } + +#if 0 + printf("mem = 0x%05x, mem0_start = 0x%05x, mem0_end = 0x%05x\n", + gfx.mem, gfx_config.mem0_start, gfx_config.mem0_end + ); +#endif + + if(open(file) == -1) return 1; + + // leave room for later alignment + gfx_config.archive_start = gfx_config.mem0_start + 0x10; + + // read at most that much + file_size = file_max = gfx_config.mem0_end - gfx_config.archive_start; + + if(fd.file_size != -1 && fd.file_size > file_size) return 1; + + // if we have the real size, use it + if(fd.file_size != -1) file_size = fd.file_size; + + file_size = read32(gfx_config.archive_start, file_size); + + if(!file_size || file_size == file_max) return 1; + + gfx_config.archive_end = gfx_config.archive_start + file_size; + + // update free mem pointer & align it a bit + gfx_config.mem0_start = (gfx_config.archive_end + 3) & ~3; + + // locate file inside cpio archive + if(!(code_start = find_file(gfx_config.archive_start, file_size, &file_start, &file_len))) { + printf("%s: invalid file format\n", file); + + return 1; + } + +#if 0 + printf("code_start = 0x%x, archive_start = 0x%x, file size = 0x%x, file_start = 0x%x, file_len = 0x%x\n", + code_start, gfx_config.archive_start, file_size, file_start, file_len + ); +#endif + + if((ofs = (gfx_config.archive_start + file_start + code_start) & 0xf)) { + printf("oops: needs to be aligned!\n"); + + memcpy32(gfx_config.archive_start - ofs, gfx_config.archive_start, file_size); + gfx_config.archive_start -= ofs; + gfx_config.archive_end -= ofs; + + return 1; + } + + gfx_config.file = gfx_config.archive_start + file_start; + gfx.code_seg = (gfx_config.file + code_start) >> 4; + + for(u = 0; u < sizeof gfx.jmp_table / sizeof *gfx.jmp_table; u++) { + gfx.jmp_table[u] = (gfx.code_seg << 16) + _mem16(gfx_config.file + code_start + 2 * u); + } + +#if 0 + for(u = 0; u < sizeof gfx.jmp_table / sizeof *gfx.jmp_table; u++) { + printf("%d: 0x%08x\n", u, gfx.jmp_table[u]); + } +#endif + + // we are ready to start + + r.esi = comboot + (uint32_t) &gfx_config; + farcall(gfx.jmp_table[GFX_BC_INIT], &r); + + if((r.eflags & X86_CF)) { + printf("graphics initialization failed\n"); + + return 1; + } + + return 0; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void gfx_done() +{ + x86regs_t r; + + farcall(gfx.jmp_table[GFX_BC_DONE], &r); +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// +// return: +// boot menu index (-1: go to text mode prompt) +// +int gfx_input() +{ + x86regs_t r; + + r.edi = comboot + (uint32_t) cmdline; + r.ecx = sizeof cmdline; + r.eax = timeout * 182 / 100; + timeout = 0; // use timeout only first time + farcall(gfx.jmp_table[GFX_BC_INPUT], &r); + if((r.eflags & X86_CF)) r.eax = 1; + + if(r.eax == 1) return -1; + + return r.ebx; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int gfx_menu_init() +{ + x86regs_t r; + + r.esi = comboot + (uint32_t) &menu; + farcall(gfx.jmp_table[GFX_BC_MENU_INIT], &r); + + return 0; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void gfx_infobox(int type, char *str1, char *str2) +{ + gfx_infobox32(type, comboot + (uint32_t) str1, str2 ? comboot + (uint32_t) str2 : 0); +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void gfx_infobox32(int type, uint32_t str1, uint32_t str2) +{ + x86regs_t r; + + r.eax = type; + r.esi = str1; + r.edi = str2; + farcall(gfx.jmp_table[GFX_BC_INFOBOX_INIT], &r); + r.edi = r.eax = 0; + farcall(gfx.jmp_table[GFX_BC_INPUT], &r); + farcall(gfx.jmp_table[GFX_BC_INFOBOX_DONE], &r); +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Load & run kernel. +// +// Returns only on error. +// +void boot() +{ + x86regs_t r; + + r.es = comboot >> 4; + r.ebx = (uint32_t) cmdline; + r.edi = kernel_start; // kernel load address + r.edx = initrd_end; // end of initrd load area (or 0) + r.esi = (uint32_t) _syslinux_hook; // cs:si = syslinux callbacks + r.eax = 0x27; // Load & run kernel (extended API) + + x86int(0x22, &r); + if(!(r.eflags & X86_CF)) return; + + r.es = comboot >> 4; + r.ebx = (uint32_t) cmdline; + r.eax = 3; // Run command + x86int(0x22, &r); +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void syslinux_hook(x86regs_t *r) +{ + uint8_t f_nr = r->eax; + + // bit 7: 0/1 continue/don't continue kernel loading in syslinux + + switch(f_nr & 0x7f) { + case 0: // abort kernel/initrd loading + gfx_infobox(0, "abort kernel loading", 0); + break; + + case 1: // kernel/initrd not found + gfx_infobox(0, "kernel not found: ", current_label); + break; + + case 2: // kernel corrupt + gfx_infobox(0, "kernel broken", 0); + break; + + case 3: // out of memory (while initrd loading) + gfx_infobox(0, "out of memory", 0); + break; + + case 4: // progress start + r->eax = r->ecx >> gfx_config.sector_shift; // kernel size in sectors + r->esi = comboot + (uint32_t) current_label; + farcall(gfx.jmp_table[GFX_BC_PROGRESS_INIT], r); + break; + + case 5: // progress increment + // always 64k + r->eax = 0x10000 >> gfx_config.sector_shift; + farcall(gfx.jmp_table[GFX_BC_PROGRESS_UPDATE], r); + break; + + case 6: // progress end: kernel loaded, stop gfxboot + farcall(gfx.jmp_table[GFX_BC_PROGRESS_DONE], r); + gfx_done(); + break; + + case 7: // stop gfxboot + gfx_done(); + break; + } +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void show_message(char *file) +{ + int c; + + if(open(file) == -1) return; + + while((c = getc()) != EOF) { + if(c < ' ' && c != '\n' && c != '\t') continue; + printf("%c", c); + } +} + + diff --git a/modules/libio.asm b/modules/libio.asm new file mode 100644 index 0000000..1f77b44 --- /dev/null +++ b/modules/libio.asm @@ -0,0 +1,854 @@ +; +; libio.asm +; +; A very minimalistic libc fragment. +; +; Copyright (c) 2009 Steffen Winterfeldt. +; +; 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, Inc., 53 Temple Place Ste 330, Boston MA 02111-1307, +; USA; either version 2 of the License, or (at your option) any later +; version; incorporated herein by reference. +; + + +; max argv elements passed to main() +%define MAX_ARGS 8 + + + bits 16 + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; interface functions +; +; Make sure not to modify registers! +; + + global printf + global getchar + global clrscr + global memcpy + global memcpy32 + global memset + global memset32 + global x86int + global farcall + global reboot + + global _gfx_cb + extern gfx_cb + global _syslinux_hook + extern syslinux_hook + + global _start + extern _main + + extern __bss_start + + section .init + +_start: + cld + + ; clear static memory + mov di,__bss_start + mov cx,sp + sub cx,di + xor ax,ax + rep stosb + + ; parse args + mov ebx,80h + movzx si,byte [bx] + mov byte [si+81h],0dh ; just make sure + xor ecx,ecx + sub sp,MAX_ARGS * 4 + mov ebp,esp + inc cx +cmd_10: + inc bx + call skip_spaces + cmp al,0dh + jz cmd_60 + imul si,cx,4 + mov [bp+si],ebx + call skip_nonspaces + mov byte [bx],0 + inc cx + cmp cx,MAX_ARGS + jae cmd_60 + cmp al,0dh + jnz cmd_10 +cmd_60: + mov byte [bx],0 + + mov [bp],ebx ; argv[0] = "" + + push ebp + push ecx + + call dword _main + + add sp,MAX_ARGS * 4 + 8 + + ret + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +skip_spaces: + mov al,[bx] + cmp al,0dh + jz skip_spaces_90 + cmp al,' ' + jz skip_spaces_10 + cmp al,9 + jnz skip_spaces_90 +skip_spaces_10: + inc bx + jmp skip_spaces +skip_spaces_90: + ret + +skip_nonspaces: + mov al,[bx] + cmp al,0dh + jz skip_nonspaces_90 + cmp al,' ' + jz skip_nonspaces_90 + cmp al,9 + jz skip_nonspaces_90 + inc bx + jmp skip_nonspaces +skip_nonspaces_90: + ret + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + section .text + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; Write text to console. +; +; args on stack +; +; Note: 32 bit call/ret! +; +printf: + mov [pf_args],sp + + pushad + + call pf_next_arg + call pf_next_arg + mov si,ax +printf_10: + lodsb + or al,al + jz printf_90 + cmp al,'%' + jnz printf_70 + mov byte [pf_pad],' ' + lodsb + dec si + cmp al,'0' + jnz printf_20 + mov [pf_pad],al +printf_20: + call get_number + mov [pf_num],ecx + lodsb + or al,al + jz printf_90 + cmp al,'%' + jz printf_70 + + cmp al,'S' + jnz printf_23 + mov byte [pf_raw_char],1 + jmp printf_24 +printf_23: + cmp al,'s' + jnz printf_30 +printf_24: + push si + + call pf_next_arg + mov si,ax + call puts + + sub ecx,[pf_num] + neg ecx + mov al,' ' + call putc_n + + pop si + + mov byte [pf_raw_char],0 + jmp printf_10 + +printf_30: + cmp al,'u' + jnz printf_35 + + mov dx,10 +printf_31: + push si + + call pf_next_arg + or dh,dh + jz printf_34 + test eax,eax + jns printf_34 + neg eax + push eax + mov al,'-' + call putc + pop eax +printf_34: + mov cl,[pf_num] + mov ch,[pf_pad] + call number + call puts + + pop si + + jmp printf_10 + +printf_35: + cmp al,'x' + jnz printf_36 + +printf_35a: + mov dx,10h + jmp printf_31 + +printf_36: + cmp al,'d' + jnz printf_37 +printf_36a: + mov dx,10ah + jmp printf_31 + +printf_37: + cmp al,'i' + jz printf_36a + + cmp al,'p' + jnz printf_40 + mov al,'0' + call putc + mov al,'x' + call putc + jmp printf_35a + +printf_40: + cmp al,'c' + jnz printf_45 + + push si + call pf_next_arg + call putc + pop si + jmp printf_10 +printf_45: + + ; more ... + + +printf_70: + call putc + jmp printf_10 +printf_90: + popad + + o32 ret + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; Get next printf arg from [pf_args]. +; +; return: +; eax arg +; +; changes no regs +; +pf_next_arg: + movzx eax,word [pf_args] + add word [pf_args],4 + mov eax,[eax] + ret + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; Convert string to number. +; +; si string +; +; return: +; ecx number +; si points past number +; CF not a number +; +get_number: + + xor ecx,ecx + mov ah,1 +get_number_10: + lodsb + or al,al + jz get_number_90 + sub al,'0' + jb get_number_90 + cmp al,9 + ja get_number_90 + movzx eax,al + imul ecx,ecx,10 + add ecx,eax + jmp get_number_10 +get_number_90: + dec si + shr ah,1 + ret + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; Convert a number to string. +; +; eax number +; cl field size +; ch padding char +; dl base +; +; return: +; si string +; +number: + mov di,num_buf + push ax + push cx + mov al,ch + mov cx,num_buf_end - num_buf + rep stosb + pop cx + pop ax + movzx cx,cl + movzx ebx,dl +number_10: + xor edx,edx + div ebx + cmp dl,9 + jbe number_20 + add dl,27h +number_20: + add dl,'0' + dec edi + mov [di],dl + or eax,eax + jz number_30 + cmp di,num_buf + ja number_10 +number_30: + mov si,di + or cx,cx + jz number_90 + cmp cx,num_buf_end - num_buf + jae number_90 + mov si,num_buf_end + sub si,cx +number_90: + ret + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; Write string. +; +; si text +; +; return: +; cx length +; +puts: + xor cx,cx +puts_10: + lodsb + or al,al + jz puts_90 + call putc + inc cx + jmp puts_10 +puts_90: + ret + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; Write char multiple times. +; +; al char +; cx count (does nothing if count <= 0) +; +putc_n: + cmp cx,0 + jle putc_n_90 + call putc + dec cx + jmp putc_n +putc_n_90: + ret + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; Print char. +; +; al char +; +putc: + pusha + cmp al,0ah + jnz putc_30 + push ax + mov al,0dh + call putc_50 + pop ax +putc_30: + call putc_50 + popa + ret +putc_50: + mov bx,7 + mov ah,0eh + int 10h + ret + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; Read char from stdin. +; +; return: +; eax char +; +; Note: 32 bit call/ret! +; +getchar: + pushad + mov ah,10h + int 16h + mov [gc_tmp],al + popad + movzx eax,byte [gc_tmp] + o32 ret + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; Clear screen. +; +; Note: 32 bit call/ret! +; +clrscr: + pushad + push es + push word 40h + pop es + mov ax,600h + mov bh,7 + xor cx,cx + mov dl,[es:4ah] + or dl,dl + jnz clrscr_20 + mov dl,80 +clrscr_20: + dec dl + mov dh,[es:84h] + or dh,dh + jnz clrscr_30 + mov dh,24 +clrscr_30: + int 10h + mov ah,2 + mov bh,[es:62h] + xor dx,dx + int 10h + pop es + popad + o32 ret + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; dst = memcpy(dst, src, size). +; +; args on stack +; +; return: +; eax dst +; +; Note: 32 bit call/ret! +; +memcpy: + pushad + + mov edi,[esp+0x20+4] + mov esi,[esp+0x20+8] + mov ecx,[esp+0x20+12] + + rep movsb + + popad + + mov eax,[esp+4] + + o32 ret + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; dst = memset(dst, val, size). +; +; args on stack +; +; return: +; eax dst +; +; Note: 32 bit call/ret! +; +memset: + pushad + + mov edi,[esp+0x20+4] + mov al,[esp+0x20+8] + mov ecx,[esp+0x20+12] + + rep stosb + + popad + + mov eax,[esp+4] + + o32 ret + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; dst = memset32(dst, val, size). +; +; args on stack +; +; return: +; eax dst +; +; Note: 32 bit call/ret! +; +memset32: + pushad + + push es + + mov ebx,[esp+0x22+4] ; edi + mov al,[esp+0x22+8] + mov edx,[esp+0x22+12] + +memset32_20: + mov edi,ebx + mov ecx,ebx + and di,0fh + shr ecx,4 + mov es,cx + + mov ecx,0fff0h + cmp edx,ecx + ja memset32_40 + mov ecx,edx +memset32_40: + add ebx,ecx + sub edx,ecx + + rep stosb + + jnz memset32_20 + + pop es + + popad + + mov eax,[esp+4] + + o32 ret + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; dst = memcpy32(dst, src, size). +; +; dst, src are 32bit linear addresses +; +; args on stack +; +; return: +; eax dst +; +; Note: 32 bit call/ret! +; +memcpy32: + pushad + + push ds + push es + + mov ebx,[esp+0x24+4] ; edi + mov eax,[esp+0x24+8] ; esi + mov edx,[esp+0x24+12] + +memcpy32_20: + mov edi,ebx + mov ecx,ebx + and di,0fh + shr ecx,4 + mov es,cx + + mov esi,eax + mov ecx,eax + and si,0fh + shr ecx,4 + mov ds,cx + + mov ecx,0fff0h + cmp edx,ecx + ja memcpy32_40 + mov ecx,edx +memcpy32_40: + add ebx,ecx + add eax,ecx + sub edx,ecx + + rep movsb + + jnz memcpy32_20 + + pop es + pop ds + + popad + + mov eax,[esp+4] + + o32 ret + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; x86int(int, *regs). +; +; args on stack +; +; Note: 32 bit call/ret! +; +x86int: + pushad + + mov al,[esp+0x20+4] + mov [x86int_p],al + mov ebx,[esp+0x20+8] + + mov ecx,[bx+8] + mov edx,[bx+0ch] + mov esi,[bx+10h] + mov edi,[bx+14h] + mov ebp,[bx+18h] + mov ah,[bx+1ch] ; eflags + sahf + mov eax,[bx] + + mov es,[bx+22h] + mov fs,[bx+24h] + mov gs,[bx+26h] + mov ds,[bx+20h] + + mov ebx,[cs:bx+4] + + int 0h +x86int_p equ $-1 + + push ebx + mov ebx,[esp+0x24+8] + pop dword [cs:bx+4] + + mov [cs:bx],eax + mov [cs:bx+20h],ds + + mov ax,cs + mov ds,ax + + mov [cs:bx+22h],es + mov [cs:bx+24h],fs + mov [cs:bx+26h],gs + + mov es,ax + mov fs,ax + mov gs,ax + + mov [bx+8],ecx + mov [bx+0ch],edx + mov [bx+10h],esi + mov [bx+14h],edi + mov [bx+18h],ebp + pushfd + pop dword [bx+1ch] + + popad + + o32 ret + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; farcall(addr, *regs). +; +; args on stack +; +; Note: 32 bit call/ret! +; +farcall: + pushad + + mov ebx,[esp+0x20+8] + + mov ecx,[bx+8] + mov edx,[bx+0ch] + mov esi,[bx+10h] + mov edi,[bx+14h] + mov ebp,[bx+18h] + mov ah,[bx+1ch] ; eflags + sahf + mov eax,[bx] + + mov [farcall_stack],sp + sub word [farcall_stack],1000h ; 4k stack should be enough for gfxboot + mov [farcall_stack+2],ss + + mov es,[bx+22h] + mov fs,[bx+24h] + mov gs,[bx+26h] + mov ds,[bx+20h] + + mov ebx,[cs:bx+4] + + call far [esp+0x20+4] + + push ebx + mov ebx,[esp+0x24+8] + pop dword [cs:bx+4] + + mov [cs:bx],eax + mov [cs:bx+20h],ds + + mov ax,cs + mov ds,ax + + mov [cs:bx+22h],es + mov [cs:bx+24h],fs + mov [cs:bx+26h],gs + + mov es,ax + mov fs,ax + mov gs,ax + + mov [bx+8],ecx + mov [bx+0ch],edx + mov [bx+10h],esi + mov [bx+14h],edi + mov [bx+18h],ebp + pushfd + pop dword [bx+1ch] + + popad + + o32 ret + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; wrapper around gfx_cb() +; +; we need to switch stack to ensure ss = cs = ds = es for gcc +; +_gfx_cb: + push cs + pop ds + push cs + pop es + mov [cb_stack],sp + mov [cb_stack+2],ss + lss sp,[farcall_stack] + sub sp,28h ; sizeof x86regs_t + mov [esp+18h],ebp + mov ebp,esp + push ebp + mov [bp],eax + mov [bp+4],ebx + mov [bp+8],ecx + mov [bp+0ch],edx + mov [bp+10h],esi + mov [bp+14h],edi + call dword gfx_cb + lea ebp,[esp+4] + mov eax,[bp] + mov ebx,[bp+4] + mov ecx,[bp+8] + mov edx,[bp+0ch] + mov esi,[bp+10h] + mov edi,[bp+14h] + mov ebp,[bp+18h] + lss sp,[cb_stack] + retf + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; wrapper around syslinux_hook() +; +; ensure cs = ds = es for gcc +; +_syslinux_hook: + push cs + pop ds + push cs + pop es + sub sp,28h ; sizeof x86regs_t + mov [esp+18h],ebp + mov ebp,esp + push ebp + mov [bp],eax + mov [bp+4],ebx + mov [bp+8],ecx + mov [bp+0ch],edx + mov [bp+10h],esi + mov [bp+14h],edi + call dword syslinux_hook + lea ebp,[esp+4] + mov eax,[bp] + mov ebx,[bp+4] + mov ecx,[bp+8] + mov edx,[bp+0ch] + mov esi,[bp+10h] + mov edi,[bp+14h] + mov ebp,[bp+18h] + add sp,28h+4 + retf + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +reboot: + mov word [472h],1234h + jmp 0ffffh:0 + int 19h + jmp $ + + +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + section .data + +farcall_stack dd 0 +cb_stack dd 0 + +; buffer for number conversions +; must be large enough for ps_status_info() +num_buf times 23h db 0 +num_buf_end db 0 + +; temp data for printf +pf_args dw 0 +pf_num dd 0 +pf_sig db 0 +pf_pad db 0 +pf_raw_char db 0 +gc_tmp db 0 + diff --git a/modules/libio.h b/modules/libio.h new file mode 100644 index 0000000..16af520 --- /dev/null +++ b/modules/libio.h @@ -0,0 +1,133 @@ +/* + * + * libio.h include file for libio + * + * Copyright (c) 2009 Steffen Winterfeldt. + * + * 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, Inc., 53 Temple Place Ste 330, Boston MA + * 02111-1307, USA; either version 2 of the License, or (at your option) any + * later version; incorporated herein by reference. + * + */ + +#ifndef _LIBIO_H +#define _LIBIO_H + + +asm(".code16gcc\n"); + + +#define int8_t char +#define int16_t short +#define int32_t int +#define int64_t long long +#define uint8_t unsigned char +#define uint16_t unsigned short +#define uint32_t unsigned +#define uint64_t unsigned long long + +#define X86_CF 0x0001 +#define X86_PF 0x0004 +#define X86_AF 0x0010 +#define X86_ZF 0x0040 +#define X86_SF 0x0080 +#define X86_TF 0x0100 +#define X86_IF 0x0200 +#define X86_DF 0x0400 +#define X86_OF 0x0800 + +#define EOF -1 + +#define main _main + + +typedef struct __attribute ((packed)) { + uint32_t eax, ebx, ecx, edx, esi, edi, ebp, eflags; + uint16_t ds, es, fs, gs; +} x86regs_t; + + +static inline uint16_t comboot_seg(void) +{ + uint16_t u; + + asm("mov %%cs, %0" : "=r" (u)); + + return u; +} + + +static inline uint8_t _mem8(uint32_t p) +{ + uint8_t u; + + asm( + "movl %1,%%esi\n" + "shrl $4,%%esi\n" + "mov %%si,%%fs\n" + "movl %1,%%esi\n" + "and $15, %%si\n" + "movb %%fs:(%%si),%0\n" + : "=abcd" (u) : "r" (p) : "si" + ); + + return u; +} + + +static inline uint16_t _mem16(uint32_t p) +{ + uint16_t u; + + asm( + "movl %1,%%esi\n" + "shrl $4,%%esi\n" + "mov %%si,%%fs\n" + "movl %1,%%esi\n" + "and $15, %%si\n" + "movw %%fs:(%%si),%0\n" + : "=r" (u) : "r" (p) : "si" + ); + + return u; +} + + +static inline uint32_t _mem32(uint32_t p) +{ + uint32_t u; + + asm( + "movl %1,%%esi\n" + "shrl $4,%%esi\n" + "mov %%si,%%fs\n" + "movl %1,%%esi\n" + "and $15, %%si\n" + "movl %%fs:(%%si),%0\n" + : "=r" (u) : "r" (p) : "si" + ); + + return u; +} + + +int _main(int argc, char **argv); +void printf(char *format, ...) __attribute__ ((format (printf, 1, 2))); +int getchar(void); +void clrscr(void); +void *memcpy(void *dest, const void *src, int n); +uint32_t memcpy32(uint32_t dest, uint32_t src, int n); +void *memset(void *dest, int c, int n); +uint32_t memset32(uint32_t dest, int c, int n); +void x86int(unsigned intr, x86regs_t *regs); +void farcall(uint32_t seg_ofs, x86regs_t *regs); +void gfx_cb(x86regs_t *r); +void _gfx_cb(void); +void syslinux_hook(x86regs_t *r); +void _syslinux_hook(void); +void reboot(void); + +#endif /* _LIBIO_H */ +