diff options
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | com32/menu/menumain.c | 5 | ||||
-rw-r--r-- | core/diskstart.inc | 8 | ||||
-rw-r--r-- | core/extlinux.asm | 4 | ||||
-rw-r--r-- | core/pxelinux.asm | 34 | ||||
-rw-r--r-- | memdisk/Makefile | 4 | ||||
-rw-r--r-- | memdisk/conio.c | 41 | ||||
-rw-r--r-- | memdisk/conio.h | 2 | ||||
-rw-r--r-- | memdisk/memdisk.h | 74 | ||||
-rw-r--r-- | memdisk/memdisk16.asm | 237 | ||||
-rw-r--r-- | memdisk/memmove.S | 139 | ||||
-rw-r--r-- | memdisk/msetup.c | 11 | ||||
-rw-r--r-- | memdisk/setup.c | 216 | ||||
-rw-r--r-- | memdisk/start32.S | 74 | ||||
-rw-r--r-- | memdisk/unzip.c | 14 |
15 files changed, 600 insertions, 266 deletions
@@ -7,6 +7,9 @@ Changes in 3.82: * ISOLINUX: deal with systems which return from INT 13h with interrupts disabled. * Do not invoke the idle handler during large file loads. + * Simple menu: make ONTIMEOUT work with MENU HIDDEN. + * PXELINUX: handle TFTP servers which have extra NULs at the + end of an OACK packet. Changes in 3.81: * Shuffler: fix bug in real-mode entry. This affected a diff --git a/com32/menu/menumain.c b/com32/menu/menumain.c index b86e9b4d..82f0018c 100644 --- a/com32/menu/menumain.c +++ b/com32/menu/menumain.c @@ -724,7 +724,10 @@ static const char *do_hidden_menu(void) } } - return cm->menu_entries[cm->defentry]->cmdline; /* Default entry */ + if (cm->ontimeout) + return cm->ontimeout; + else + return cm->menu_entries[cm->defentry]->cmdline; /* Default entry */ } static const char *run_menu(void) diff --git a/core/diskstart.inc b/core/diskstart.inc index b8047264..d7279028 100644 --- a/core/diskstart.inc +++ b/core/diskstart.inc @@ -501,6 +501,7 @@ ldlinux_ent: ; jmp 0:.next .next: + sti ; In case of broken INT 13h BIOSes ; ; Tell the user we got this far @@ -629,11 +630,16 @@ writestr_early: ret +; ; getlinsecsr: save registers, call getlinsec, restore registers +; Save/restore the flags, too, especially IF. ; -getlinsecsr: pushad +getlinsecsr: + pushfd + pushad call getlinsec popad + popfd ret ; diff --git a/core/extlinux.asm b/core/extlinux.asm index 3a054b22..20ba9c6a 100644 --- a/core/extlinux.asm +++ b/core/extlinux.asm @@ -121,7 +121,7 @@ Files resb MAX_OPEN*open_file_t_size mov bx,SuperBlock mov eax,1024 >> SECTOR_SHIFT mov bp,ax - call getlinsec + call getlinsecsr ; ; Compute some values... @@ -212,7 +212,7 @@ getonesec_ext: getlinsec_ext: cmp eax,[SecPerClust] - jae getlinsec ; Nothing fancy + jae getlinsecsr ; Nothing fancy ; If we get here, at least part of what we want is in the ; zero block. Zero one sector at a time and loop. diff --git a/core/pxelinux.asm b/core/pxelinux.asm index b866369c..3f7cb8c6 100644 --- a/core/pxelinux.asm +++ b/core/pxelinux.asm @@ -1114,7 +1114,17 @@ searchdir: ; SI -> first byte of options; [E]CX -> byte count .parse_oack: jcxz .done_pkt ; No options acked + .get_opt_name: + ; If we find an option which starts with a NUL byte, + ; (a null option), we're either seeing garbage that some + ; TFTP servers add to the end of the packet, or we have + ; no clue how to parse the rest of the packet (what is + ; an option name and what is a value?) In either case, + ; discard the rest. + cmp byte [si],0 + je .done_pkt + mov di,si mov bx,si .opt_name_loop: lodsb @@ -1124,10 +1134,10 @@ searchdir: stosb loop .opt_name_loop ; We ran out, and no final null - jmp .err_reply + jmp .done_pkt ; Ignore runt option .got_opt_name: ; si -> option value dec cx ; bytes left in pkt - jz .err_reply ; Option w/o value + jz .done_pkt ; Option w/o value, ignore ; Parse option pointed to by bx; guaranteed to be ; null-terminated. @@ -1150,7 +1160,8 @@ searchdir: pop si pop cx - jmp .err_reply ; Non-negotiated option returned + ; Non-negotiated option returned, no idea what it means... + jmp .err_reply .get_value: pop si ; si -> option value pop cx ; cx -> bytes left in pkt @@ -1230,13 +1241,13 @@ searchdir: pop es jmp .done_pkt -.err_reply: ; Option negotiation error. Send ERROR reply. +.err_reply: ; TFTP protocol error. Send ERROR reply. ; ServerIP and gateway are already programmed in mov si,[bp-6] mov ax,[si+tftp_remoteport] mov word [pxe_udp_write_pkt.rport],ax - mov word [pxe_udp_write_pkt.buffer],tftp_opt_err - mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len + mov word [pxe_udp_write_pkt.buffer],tftp_proto_err + mov word [pxe_udp_write_pkt.buffersize],tftp_proto_err_len mov di,pxe_udp_write_pkt mov bx,PXENV_UDP_WRITE call pxenv @@ -2727,12 +2738,13 @@ tftp_opt_table: tftp_opts equ ($-tftp_opt_table)/6 ; -; Error packet to return on options negotiation error +; Error packet to return on TFTP protocol error +; Most of our errors are OACK parsing errors, so use that error code ; -tftp_opt_err dw TFTP_ERROR ; ERROR packet - dw TFTP_EOPTNEG ; ERROR 8: bad options - db 'tsize option required', 0 ; Error message -tftp_opt_err_len equ ($-tftp_opt_err) +tftp_proto_err dw TFTP_ERROR ; ERROR packet + dw TFTP_EOPTNEG ; ERROR 8: OACK error + db 'TFTP protocol error', 0 ; Error message +tftp_proto_err_len equ ($-tftp_proto_err) alignz 4 ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet diff --git a/memdisk/Makefile b/memdisk/Makefile index 3bb97ffd..e1a89351 100644 --- a/memdisk/Makefile +++ b/memdisk/Makefile @@ -38,10 +38,10 @@ endif # Important: init.o16 must be first!! OBJS16 = init.o16 init32.o OBJS32 = start32.o setup.o msetup.o e820func.o conio.o memcpy.o memset.o \ - unzip.o memdisk_chs.o memdisk_edd.o + memmove.o unzip.o memdisk_chs.o memdisk_edd.o CSRC = setup.c msetup.c e820func.c conio.c unzip.c -SSRC = start32.S memcpy.S memset.S +SSRC = start32.S memcpy.S memset.S memmove.S NASMSRC = memdisk_chs.asm memdisk_edd.asm memdisk16.asm all: memdisk # e820test diff --git a/memdisk/conio.c b/memdisk/conio.c index 9ccc8e2d..d1f0862c 100644 --- a/memdisk/conio.c +++ b/memdisk/conio.c @@ -30,7 +30,7 @@ int putchar(int ch) } regs.eax.w[0] = 0x0e00 | (ch & 0xff); - syscall(0x10, ®s, NULL); + intcall(0x10, ®s, NULL); return ch; } @@ -109,11 +109,11 @@ static char *number(char *str, long num, int base, int size, int precision, int type) { char c, sign, tmp[66]; - const char *digits = "0123456789abcdefghijklmnopqrstuvwxyz"; + const char *digits = "0123456789abcdef"; int i; if (type & LARGE) - digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + digits = "0123456789ABCDEF"; if (type & LEFT) type &= ~ZEROPAD; if (base < 2 || base > 36) @@ -173,9 +173,6 @@ static char *number(char *str, long num, int base, int size, int precision, return str; } -/* Forward decl. needed for IP address printing stuff... */ -int sprintf(char *buf, const char *fmt, ...); - int vsprintf(char *buf, const char *fmt, va_list args) { int len; @@ -346,6 +343,7 @@ repeat: return str - buf; } +#if 0 int sprintf(char *buf, const char *fmt, ...) { va_list args; @@ -356,18 +354,41 @@ int sprintf(char *buf, const char *fmt, ...) va_end(args); return i; } +#endif + +int vprintf(const char *fmt, va_list args) +{ + char printf_buf[2048]; + int printed; + + printed = vsprintf(printf_buf, fmt, args); + puts(printf_buf); + return printed; +} int printf(const char *fmt, ...) { - char printf_buf[1024]; va_list args; int printed; va_start(args, fmt); - printed = vsprintf(printf_buf, fmt, args); + printed = vprintf(fmt, args); va_end(args); + return printed; +} - puts(printf_buf); +/* + * Jump here if all hope is gone... + */ +void __attribute__ ((noreturn)) die(const char *fmt, ...) +{ + va_list ap; - return printed; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + + sti(); + for (;;) + asm volatile("hlt"); } diff --git a/memdisk/conio.h b/memdisk/conio.h index 9fa89919..9775e62a 100644 --- a/memdisk/conio.h +++ b/memdisk/conio.h @@ -25,7 +25,9 @@ int putchar(int); int puts(const char *); +int vprintf(const char *, va_list ap); int printf(const char *, ...); +void __attribute__((noreturn)) die(const char *, ...); unsigned int atou(const char *); #endif diff --git a/memdisk/memdisk.h b/memdisk/memdisk.h index 1ab2c000..b6b277a8 100644 --- a/memdisk/memdisk.h +++ b/memdisk/memdisk.h @@ -24,21 +24,63 @@ /* We use the com32 interface for calling 16-bit code */ #include <com32.h> -/* The real-mode segment */ -#define LOW_SEG 0x0800 - #define __cdecl __attribute__((cdecl,regparm(0))) -typedef void (*syscall_t) (uint8_t, com32sys_t *, com32sys_t *); -extern __cdecl syscall_t syscall; -extern void *sys_bounce; - -/* What to call when we're dead */ -extern void __attribute__ ((noreturn)) die(void); +void __cdecl intcall(uint8_t, com32sys_t *, com32sys_t *); + +/* Structure passed in from the real-mode code */ +struct real_mode_args { + uint32_t rm_return; + uint32_t rm_intcall; + uint32_t rm_bounce; + uint32_t rm_base; + uint32_t rm_handle_interrupt; + uint32_t rm_gdt; + uint32_t rm_size; + uint32_t rm_pmjmp; + uint32_t rm_rmjmp; +}; +extern struct real_mode_args rm_args; +#define sys_bounce ((void *)rm_args.rm_bounce) + +/* This is the header in the boot sector/setup area */ +struct setup_header { + char cmdline[0x1f1]; + uint8_t setup_secs; + uint16_t syssize; + uint16_t swap_dev; + uint16_t ram_size; + uint16_t vid_mode; + uint16_t root_dev; + uint16_t boot_flag; + uint16_t jump; + char header[4]; + uint16_t version; + uint32_t realmode_swtch; + uint32_t start_sys; + uint8_t type_of_loader; + uint8_t loadflags; + uint16_t setup_move_size; + uint32_t code32_start; + uint32_t ramdisk_image; + uint32_t ramdisk_size; + uint32_t bootsect_kludge; + uint16_t head_end_ptr; + uint16_t pad1; + uint32_t cmd_line_ptr; + uint32_t initrd_addr_max; + uint32_t esdi; + uint32_t edx; + uint32_t sssp; + uint32_t csip; +}; +#define shdr ((struct setup_header *)rm_args.rm_base) /* Standard routines */ -#define memcpy(a,b,c) __builtin_memcpy(a,b,c) -#define memset(a,b,c) __builtin_memset(a,b,c) +void *memcpy(void *, const void *, size_t); +void *memset(void *, int, size_t); +void *memmove(void *, const void *, size_t); + #define strcpy(a,b) __builtin_strcpy(a,b) static inline size_t strlen(const char *__a) @@ -75,6 +117,16 @@ static inline int memcmp(const void *__a, const void *__b, unsigned int __n) return 0; } +static inline void sti(void) +{ + asm volatile("sti"); +} + +static inline void cli(void) +{ + asm volatile("cli"); +} + /* Decompression */ extern int check_zip(void *indata, uint32_t size, uint32_t * zbytes_p, uint32_t * dbytes_p, uint32_t * orig_crc, diff --git a/memdisk/memdisk16.asm b/memdisk/memdisk16.asm index d43404e8..0c5b9973 100644 --- a/memdisk/memdisk16.asm +++ b/memdisk/memdisk16.asm @@ -2,6 +2,7 @@ ;; ----------------------------------------------------------------------- ;; ;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin ;; ;; 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 @@ -29,11 +30,12 @@ BOUNCE_SEG equ (MY_CS+0x1000) %define DO_WBINVD 0 -%define STACK_HEAP_SIZE (128*1024) - section .rodata align=16 section .data align=16 section .bss align=16 + section .stack align=16 nobits +stack resb 512 +stack_end equ $ ;; ----------------------------------------------------------------------- ;; Kernel image header @@ -78,6 +80,8 @@ ramdisk_max dd 0xffffffff ; Highest allowed ramdisk address ; b_esdi dd 0 ; ES:DI for boot sector invocation b_edx dd 0 ; EDX for boot sector invocation +b_sssp dd 0 ; SS:SP on boot sector invocation +b_csip dd 0 ; CS:IP on boot sector invocation section .rodata memdisk_version: @@ -106,7 +110,7 @@ start: rep movsd mov ds,ax mov ss,ax - xor esp,esp ; Stack at top of 64K segment + mov esp,stack_end jmp MY_CS:.next .next: @@ -153,9 +157,9 @@ copy_cmdline: mov ds,si ; Make all the segments consistent mov fs,si mov gs,si - mov ss,si - mov esp,0x7C00 ; Good place for SP to start out - call 0:0x7C00 + lss sp,[cs:b_sssp] + movzx esp,sp + call far [cs:b_csip] int 18h ; A far return -> INT 18h ; @@ -166,7 +170,9 @@ copy_cmdline: ; segments, but this stuff is painful enough as it is without having to rely ; on everything happening "as it ought to." ; - section .rodata +DummyTSS equ 0x580 ; Hopefully safe place in low mmoery + + section .data ; desc base, limit, flags %macro desc 3 @@ -179,15 +185,16 @@ call32_gdt: dw call32_gdt_size-1 ; Null descriptor - contains GDT .adj1: dd call32_gdt+CS_BASE ; pointer for LGDT instruction dw 0 - ; 0008: Code segment, use16, readable, dpl 0, base CS_BASE, 64K + ; 0008: Dummy TSS to make Intel VT happy + ; Should never be actually accessed... + desc DummyTSS, 103, 0x8089 + + ; 0010: Code segment, use16, readable, dpl 0, base CS_BASE, 64K desc CS_BASE, 0xffff, 0x009b - ; 0010: Data segment, use16, read/write, dpl 0, base CS_BASE, 64K + ; 0018: Data segment, use16, read/write, dpl 0, base CS_BASE, 64K desc CS_BASE, 0xffff, 0x0093 - ; 0018: Data segment, use16, read/write, dpl 0, base 0, 4G - desc 0, 0xfffff, 0x809b - ; 0020: Code segment, use32, read/write, dpl 0, base 0, 4G desc 0, 0xfffff, 0xc09b @@ -200,13 +207,12 @@ err_a20: db 'ERROR: A20 gate not responding!',13,10,0 section .bss alignb 4 -SavedSSSP resd 1 ; Place to save SS:SP Return resd 1 ; Return value -A20Test resw 1 ; Space to test A20 +SavedSP resw 1 ; Place to save SP A20Tries resb 1 section .data - alignb 4 + align 4, db 0 Target dd 0 ; Target address Target_Seg dw 20h ; Target CS @@ -381,22 +387,38 @@ a20_done: popad ; This routine tests if A20 is enabled (ZF = 0). This routine ; must not destroy any register contents. ; + +; This is the INT 1Fh vector, which is standard PCs is used by the +; BIOS when the screen is in graphics mode. Even if it is, it points to +; data, not code, so it should be safe enough to fiddle with. +A20Test equ (1Fh*4) + a20_test: + push ds push es push cx - push ax - mov cx,0FFFFh ; HMA = segment 0FFFFh - mov es,cx + push eax + xor ax,ax + mov ds,ax ; DS == 0 + dec ax + mov es,ax ; ES == 0FFFFh mov cx,32 ; Loop count - mov ax,[A20Test] -.a20_wait: inc ax - mov [A20Test],ax - io_delay ; Serialize, and fix delay - cmp ax,[es:A20Test+CS_BASE+10h] + mov eax,[A20Test] + cmp eax,[es:A20Test+10h] + jne .a20_done + push eax +.a20_wait: + inc eax + mov [A20Test],eax + io_delay + cmp eax,[es:A20Test+10h] loopz .a20_wait -.a20_done: pop ax + pop dword [A20Test] ; Restore original value +.a20_done: + pop eax pop cx pop es + pop ds ret disable_a20: @@ -507,53 +529,65 @@ pm_idt resb 4096 ; Protected-mode IDT, followed by interrupt stubs pm_entry: equ 0x100000 section .rodata - align 4, db 0 -call32_pmidt: - dw 8*256 ; Limit - dd pm_idt+CS_BASE ; Address - + align 2, db 0 call32_rmidt: dw 0ffffh ; Limit dd 0 ; Address + section .data + alignb 2 +call32_pmidt: + dw 8*256 ; Limit + dd 0 ; Address (entered later) + section .text ; ; This is the main entrypoint in this function ; init32: - mov ebx,call32_call_start+CS_BASE ; Where to go in PM + mov bx,call32_call_start ; Where to go in PM +; +; Enter protected mode. BX contains the entry point relative to the +; real-mode CS. +; call32_enter_pm: mov ax,cs mov ds,ax + movzx ebp,ax + shl ebp,4 ; EBP <- CS_BASE + movzx ebx,bx + add ebx,ebp ; entry point += CS_BASE cli - mov [SavedSSSP],sp - mov [SavedSSSP+2],ss + mov [SavedSP],sp cld - call a20_test - jnz .a20ok call enable_a20 - -.a20ok: - lgdt [call32_gdt] ; Set up GDT - lidt [call32_pmidt] ; Set up the IDT + mov byte [call32_gdt+8+5],89h ; Mark TSS unbusy + o32 lgdt [call32_gdt] ; Set up GDT + o32 lidt [call32_pmidt] ; Set up IDT mov eax,cr0 or al,1 mov cr0,eax ; Enter protected mode - jmp 20h:dword .in_pm+CS_BASE + jmp 20h:strict dword .in_pm+CS_BASE +.pm_jmp equ $-6 + bits 32 .in_pm: xor eax,eax ; Available for future use... mov fs,eax mov gs,eax + lldt ax mov al,28h ; Set up data segments mov es,eax mov ds,eax mov ss,eax - mov esp,[PMESP+CS_BASE] ; Load protmode %esp if available + mov al,08h + ltr ax + + mov esp,[ebp+PMESP] ; Load protmode %esp if available jmp ebx ; Go to where we need to go ; @@ -561,66 +595,22 @@ call32_enter_pm: ; call32_call_start: ; - ; Point the stack into low memory - ; We have: this segment, bounce buffer, then stack+heap - ; - mov esp, CS_BASE + 0x20000 + STACK_HEAP_SIZE - and esp, ~0xf - - ; - ; Set up the protmode IDT and the interrupt jump buffers - ; - mov edi,pm_idt+CS_BASE - - ; Form an interrupt gate descriptor - ; WARNING: This is broken if pm_idt crosses a 64K boundary; - ; however, it can't because of the alignment constraints. - mov ebx,pm_idt+CS_BASE+8*256 - mov eax,0x0020ee00 - xchg ax,bx - xor ecx,ecx - inc ch ; ecx <- 256 - - push ecx -.make_idt: - stosd - add eax,8 - xchg eax,ebx - stosd - xchg eax,ebx - loop .make_idt - - pop ecx - - ; Each entry in the interrupt jump buffer contains - ; the following instructions: + ; Set up a temporary stack in the bounce buffer; + ; start32.S will override this to point us to the real + ; high-memory stack. ; - ; 00000000 60 pushad - ; 00000001 B0xx mov al,<interrupt#> - ; 00000003 E9xxxxxxxx jmp call32_handle_interrupt - - mov eax,0xe900b060 - mov ebx,call32_handle_interrupt+CS_BASE - sub ebx,edi - -.make_ijb: - stosd - sub [edi-2],cl ; Interrupt # - xchg eax,ebx - sub eax,8 - stosd - xchg eax,ebx - loop .make_ijb - - ; Now everything is set up for interrupts... - + mov esp, (BOUNCE_SEG << 4) + 0x10000 + + push dword call32_enter_rm.rm_jmp+CS_BASE + push dword call32_enter_pm.pm_jmp+CS_BASE + push dword stack_end ; RM size + push dword call32_gdt+CS_BASE + push dword call32_handle_interrupt+CS_BASE + push dword CS_BASE ; Segment base push dword (BOUNCE_SEG << 4) ; Bounce buffer address push dword call32_syscall+CS_BASE ; Syscall entry point - sti ; Interrupts OK now - call pm_entry-CS_BASE ; Run the program... - ; ... on return ... - mov [Return+CS_BASE],eax + call pm_entry-CS_BASE ; Run the program... ; ... fall through to call32_exit ... @@ -628,15 +618,24 @@ call32_exit: mov bx,call32_done ; Return to command loop call32_enter_rm: + ; Careful here... the PM code may have relocated the + ; entire RM code, so we need to figure out exactly + ; where we are executing from. If the PM code has + ; relocated us, it *will* have adjusted the GDT to + ; match, though. + call .here +.here: pop ebp + sub ebp,.here + o32 sidt [ebp+call32_pmidt] cli cld - mov [PMESP+CS_BASE],esp ; Save exit %esp + mov [ebp+PMESP],esp ; Save exit %esp xor esp,esp ; Make sure the high bits are zero - jmp 08h:.in_pm16 ; Return to 16-bit mode first + jmp 10h:.in_pm16 ; Return to 16-bit mode first bits 16 .in_pm16: - mov ax,10h ; Real-mode-like segment + mov ax,18h ; Real-mode-like segment mov es,ax mov ds,ax mov ss,ax @@ -648,20 +647,21 @@ call32_enter_rm: and al,~1 mov cr0,eax jmp MY_CS:.in_rm +.rm_jmp equ $-2 .in_rm: ; Back in real mode - mov ax,cs ; Set up sane segments + mov ax,cs mov ds,ax mov es,ax mov fs,ax mov gs,ax - lss sp,[SavedSSSP] ; Restore stack + mov ss,ax + mov sp,[SavedSP] ; Restore stack jmp bx ; Go to whereever we need to go... call32_done: call disable_a20 sti - mov ax,[Return] ret ; @@ -679,7 +679,7 @@ call32_int_rm: push dword edx ; Segment:offset of IVT entry retf ; Invoke IVT routine .cont: ; ... on resume ... - mov ebx,call32_int_resume+CS_BASE + mov bx,call32_int_resume jmp call32_enter_pm ; Go back to PM ; @@ -700,7 +700,7 @@ call32_sys_rm: push es push fs push gs - mov ebx,call32_sys_resume+CS_BASE + mov bx,call32_sys_resume jmp call32_enter_pm ; @@ -741,13 +741,14 @@ call32_syscall: pushfd ; Save IF among other things... pushad ; We only need to save some, but... cld + call .here +.here: pop ebp + sub ebp,.here - movzx edi,word [SavedSSSP+CS_BASE] - movzx eax,word [SavedSSSP+CS_BASE+2] + movzx edi,word [ebp+SavedSP] sub edi,54 ; Allocate 54 bytes - mov [SavedSSSP+CS_BASE],di - shl eax,4 - add edi,eax ; Create linear address + mov [ebp+SavedSP],di + add edi,ebp ; Create linear address mov esi,[esp+11*4] ; Source regs xor ecx,ecx @@ -759,8 +760,11 @@ call32_syscall: ; encoding smaller mov eax,[ecx+eax*4] ; Get IVT entry stosd ; Save in stack frame - mov eax,call32_sys_rm.return + (MY_CS << 16) ; Return seg:offs - stosd ; Save in stack frame + mov ax,call32_sys_rm.return ; Return offset + stosw ; Save in stack frame + mov eax,ebp + shr eax,4 ; Return segment + stosw ; Save in stack frame mov eax,[edi-12] ; Return flags and eax,0x200cd7 ; Mask (potentially) unsafe flags mov [edi-12],eax ; Primary flags entry @@ -770,13 +774,12 @@ call32_syscall: jmp call32_enter_rm ; Go to real mode ; On return, the 44-byte return structure is on the - ; real-mode stack. + ; real-mode stack. call32_enter_pm will leave ebp + ; pointing to the real-mode base. call32_sys_resume: - movzx esi,word [SavedSSSP+CS_BASE] - movzx eax,word [SavedSSSP+CS_BASE+2] + movzx esi,word [ebp+SavedSP] mov edi,[esp+12*4] ; Dest regs - shl eax,4 - add esi,eax ; Create linear address + add esi,ebp ; Create linear address and edi,edi ; NULL pointer? jnz .do_copy .no_copy: mov edi,esi ; Do a dummy copy-to-self @@ -784,7 +787,7 @@ call32_sys_resume: mov cl,11 ; 44 bytes rep movsd ; Copy register block - add dword [SavedSSSP+CS_BASE],44 ; Remove from stack + add word [ebp+SavedSP],44 ; Remove from stack popad popfd diff --git a/memdisk/memmove.S b/memdisk/memmove.S new file mode 100644 index 00000000..b7ac6767 --- /dev/null +++ b/memdisk/memmove.S @@ -0,0 +1,139 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * memmove.S + * + * Reasonably efficient memmove, using aligned transfers at least + * for the destination operand. + */ + + .globl memmove + .type memmove,@function + .text +memmove: + jecxz 3f + + pushl %esi + pushl %edi + pushl %eax /* Return value */ + + movl %eax,%edi + movl %edx,%esi + + cmpl %edi,%esi + jb 1f + + /* source >= dest, forwards move */ + + /* Initial alignment */ + movl %edi,%edx + shrl $1,%edx + jnc 11f + movsb + decl %ecx +11: + movb %cl,%al + cmpl $2,%ecx + jb 13f + + shrl $1,%edx + jnc 12f + movsw + subl $2,%ecx +12: + /* Bulk transfer */ + movb %cl,%al + shrl $2,%ecx + rep; movsl + + /* Final alignment */ + testb $2,%al + jz 14f + movsw +13: +14: + testb $1,%al + jz 15f + movsb +15: + jmp 2f + + +1: + /* source < dest, backwards move */ + std + leal -1(%ecx,%esi),%esi + leal -1(%ecx,%edi),%edi + + /* Initial alignment */ + movl %edi,%edx + shrl $1,%edx + jc 21f + movsb + decl %ecx +21: + decl %esi + decl %edi + movb %cl,%al + cmpl $2,%ecx + jb 23f + shrl $1,%edx + jc 22f + movsw + subl $2,%ecx +22: + /* Bulk transfer */ + subl $2,%esi + subl $2,%edi + movb %cl,%al + shrl $2,%ecx + rep; movsl + + /* Final alignment */ + addl $2,%esi + addl $2,%edi + testb $2,%al + jz 24f + movsw +23: +24: + incl %esi + incl %edi + testb $1,%al + jz 25f + movsb +25: + cld +2: + popl %eax /* Return value */ + popl %edi + popl %esi +3: + ret + + .size memmove, .-memmove diff --git a/memdisk/msetup.c b/memdisk/msetup.c index adb35f90..f40a2c66 100644 --- a/memdisk/msetup.c +++ b/memdisk/msetup.c @@ -53,7 +53,7 @@ static inline int get_e820(void) regs.edi.w[0] = OFFS(buf); regs.es = SEG(buf); - syscall(0x15, ®s, ®s); + intcall(0x15, ®s, ®s); copied = (regs.eflags.l & 1) ? 0 : regs.ecx.l; if (regs.eax.l != 0x534d4150 || copied < 20) @@ -76,7 +76,7 @@ static inline void get_dos_mem(void) com32sys_t regs; memset(®s, 0, sizeof regs); - syscall(0x12, ®s, ®s); + intcall(0x12, ®s, ®s); insertrange(0, (uint64_t) ((uint32_t) regs.eax.w[0] << 10), 1); printf(" DOS: %d K\n", regs.eax.w[0]); } @@ -89,7 +89,7 @@ static inline int get_e801(void) memset(®s, 0, sizeof regs); regs.eax.w[0] = 0xe801; - syscall(0x15, ®s, ®s); + intcall(0x15, ®s, ®s); if (!(err = regs.eflags.l & 1)) { if (regs.eax.w[0]) { @@ -115,7 +115,7 @@ static inline int get_88(void) memset(®s, 0, sizeof regs); regs.eax.b[1] = 0x88; - syscall(0x15, ®s, ®s); + intcall(0x15, ®s, ®s); if (!(err = regs.eflags.l & 1)) { if (regs.eax.w[0]) { @@ -135,8 +135,7 @@ void get_mem(void) get_dos_mem(); if (get_e801()) { if (get_88()) { - puts("MEMDISK: Unable to obtain memory map\n"); - die(); + die("MEMDISK: Unable to obtain memory map\n"); } } } diff --git a/memdisk/setup.c b/memdisk/setup.c index 7e0ebc93..98c4b69d 100644 --- a/memdisk/setup.c +++ b/memdisk/setup.c @@ -123,38 +123,6 @@ struct patch_area { struct edd_dpt edd_dpt; }; -/* This is the header in the boot sector/setup area */ -struct setup_header { - char cmdline[0x1f1]; - uint8_t setup_secs; - uint16_t syssize; - uint16_t swap_dev; - uint16_t ram_size; - uint16_t vid_mode; - uint16_t root_dev; - uint16_t boot_flag; - uint16_t jump; - char header[4]; - uint16_t version; - uint32_t realmode_swtch; - uint32_t start_sys; - uint8_t type_of_loader; - uint8_t loadflags; - uint16_t setup_move_size; - uint32_t code32_start; - uint32_t ramdisk_image; - uint32_t ramdisk_size; - uint32_t bootsect_kludge; - uint16_t head_end_ptr; - uint16_t pad1; - uint32_t cmd_line_ptr; - uint32_t initrd_addr_max; - uint32_t esdi; - uint32_t edx; -}; - -struct setup_header *const shdr = (struct setup_header *)(LOW_SEG << 4); - /* Access to high memory */ /* Access to objects in the zero page */ @@ -209,7 +177,7 @@ static inline uint32_t rdz_32(uint32_t addr) #define CMD_BOOL ((char *)-2) /* Found boolean option */ #define CMD_HASDATA(X) ((int)(X) >= 0) -const char *getcmditem(const char *what) +static const char *getcmditem(const char *what) { const char *p; const char *wp = what; @@ -277,18 +245,23 @@ void unzip_if_needed(uint32_t * where_p, uint32_t * size_p) &orig_crc, &offset) == 0) { if (offset + zbytes > size) { - /* Assertion failure; check_zip is supposed to guarantee this - never happens. */ - puts("internal error: check_zip returned nonsense\n"); - die(); + /* + * Assertion failure; check_zip is supposed to guarantee this + * never happens. + */ + die("internal error: check_zip returned nonsense\n"); } - /* Find a good place to put it: search memory ranges in descending order - until we find one that is legal and fits */ + /* + * Find a good place to put it: search memory ranges in descending + * order until we find one that is legal and fits + */ okmem = 0; for (i = nranges - 1; i >= 0; i--) { - /* We can't use > 4G memory (32 bits only.) Truncate to 2^32-1 - so we don't have to deal with funny wraparound issues. */ + /* + * We can't use > 4G memory (32 bits only.) Truncate to 2^32-1 + * so we don't have to deal with funny wraparound issues. + */ /* Must be memory */ if (ranges[i].type != 1) @@ -317,16 +290,25 @@ void unzip_if_needed(uint32_t * where_p, uint32_t * size_p) if (startrange >= endrange) continue; - /* Must be large enough... don't rely on gzwhere for this (wraparound) */ + /* + * Must be large enough... don't rely on gzwhere for this + * (wraparound) + */ if (endrange - startrange < gzdatasize) continue; - /* This is where the gz image should be put if we put it in this range */ + /* + * This is where the gz image would be put if we put it in this + * range... + */ gzwhere = (endrange - gzdatasize) & ~(UNZIP_ALIGN - 1); /* Cast to uint64_t just in case we're flush with the top byte */ if ((uint64_t) where + size >= gzwhere && where < endrange) { - /* Need to move source data to avoid compressed/uncompressed overlap */ + /* + * Need to move source data to avoid compressed/uncompressed + * overlap + */ uint32_t newwhere; if (gzwhere - startrange < size) @@ -336,9 +318,7 @@ void unzip_if_needed(uint32_t * where_p, uint32_t * size_p) printf("Moving compressed data from 0x%08x to 0x%08x\n", where, newwhere); - /* Our memcpy() is OK, because we always move from a higher - address to a lower one */ - memcpy((void *)newwhere, (void *)where, size); + memmove((void *)newwhere, (void *)where, size); where = newwhere; } @@ -347,12 +327,9 @@ void unzip_if_needed(uint32_t * where_p, uint32_t * size_p) break; } - if (!okmem) { - printf - ("Not enough memory to decompress image (need 0x%08x bytes)\n", + if (!okmem) + die("Not enough memory to decompress image (need 0x%08x bytes)\n", gzdatasize); - die(); - } printf("gzip image: decompressed addr 0x%08x, len 0x%08x: ", target, gzdatasize); @@ -439,7 +416,7 @@ struct dosemu_header { #define FOUR(a,b,c,d) (((a) << 24)|((b) << 16)|((c) << 8)|(d)) -const struct geometry *get_disk_image_geometry(uint32_t where, uint32_t size) +static const struct geometry *get_disk_image_geometry(uint32_t where, uint32_t size) { static struct geometry hd_geometry; struct dosemu_header dosemu; @@ -654,16 +631,6 @@ const struct geometry *get_disk_image_geometry(uint32_t where, uint32_t size) } /* - * Jump here if all hope is gone... - */ -void __attribute__ ((noreturn)) die(void) -{ - asm volatile ("sti"); - for (;;) - asm volatile ("hlt"); -} - -/* * Find a $PnP installation check structure; return (ES << 16) + DI value */ static uint32_t pnp_install_check(void) @@ -691,17 +658,66 @@ static uint32_t pnp_install_check(void) return 0; } +/* + * Relocate the real-mode code to a new segment + */ +struct gdt_ptr { + uint16_t limit; + uint32_t base; +} __attribute__((packed)); + +static void set_seg_base(uint32_t gdt_base, int seg, uint32_t v) +{ + *(uint16_t *)(gdt_base + seg + 2) = v; + *(uint8_t *)(gdt_base + seg + 4) = v >> 16; + *(uint8_t *)(gdt_base + seg + 7) = v >> 24; +} + +static void relocate_rm_code(uint32_t newbase) +{ + uint32_t gdt_base; + uint32_t oldbase = rm_args.rm_base; + uint32_t delta = newbase - oldbase; + + cli(); + memmove((void *)newbase, (void *)oldbase, rm_args.rm_size); + + rm_args.rm_return += delta; + rm_args.rm_intcall += delta; + rm_args.rm_bounce += delta; + rm_args.rm_base += delta; + rm_args.rm_gdt += delta; + rm_args.rm_pmjmp += delta; + rm_args.rm_rmjmp += delta; + + gdt_base = rm_args.rm_gdt; + + *(uint32_t *)(gdt_base+2) = gdt_base; /* GDT self-pointer */ + + /* Segments 0x10 and 0x18 are real-mode-based */ + set_seg_base(gdt_base, 0x10, rm_args.rm_base); + set_seg_base(gdt_base, 0x18, rm_args.rm_base); + + asm volatile("lgdtl %0" : : "m" (*(char *)gdt_base)); + + *(uint32_t *)rm_args.rm_pmjmp += delta; + *(uint16_t *)rm_args.rm_rmjmp += delta >> 4; + + rm_args.rm_handle_interrupt += delta; + + sti(); +} + #define STACK_NEEDED 512 /* Number of bytes of stack */ +struct real_mode_args rm_args; + /* * Actual setup routine * Returns the drive number (which is then passed in %dl to the * called routine.) */ -__cdecl syscall_t syscall; -void *sys_bounce; - -__cdecl void setup(__cdecl syscall_t cs_syscall, void *cs_bounce) +void setup(const struct real_mode_args *rm_args_ptr) { unsigned int bin_size; char *memdisk_hook; @@ -715,21 +731,23 @@ __cdecl void setup(__cdecl syscall_t cs_syscall, void *cs_bounce) int total_size, cmdlinelen; com32sys_t regs; uint32_t ramdisk_image, ramdisk_size; + uint32_t boot_base, rm_base; int bios_drives; int do_edd = 1; /* 0 = no, 1 = yes, default is yes */ int no_bpt; /* No valid BPT presented */ + uint32_t boot_seg = 0; /* Meaning 0000:7C00 */ + uint32_t boot_len = 512; /* One sector */ + uint32_t boot_lba = 0; /* LBA of bootstrap code */ - /* Set up global variables */ - syscall = cs_syscall; - sys_bounce = cs_bounce; + /* We need to copy the rm_args into their proper place */ + memcpy(&rm_args, rm_args_ptr, sizeof rm_args); + sti(); /* ... then interrupts are safe */ /* Show signs of life */ printf("%s %s\n", memdisk_version, copyright); - if (!shdr->ramdisk_image || !shdr->ramdisk_size) { - puts("MEMDISK: No ramdisk image specified!\n"); - die(); - } + if (!shdr->ramdisk_image || !shdr->ramdisk_size) + die("MEMDISK: No ramdisk image specified!\n"); ramdisk_image = shdr->ramdisk_image; ramdisk_size = shdr->ramdisk_size; @@ -895,10 +913,8 @@ __cdecl void setup(__cdecl syscall_t cs_syscall, void *cs_bounce) printf("Total size needed = %u bytes, allocating %uK\n", total_size, (total_size + 0x3ff) >> 10); - if (total_size > dos_mem) { - puts("MEMDISK: Insufficient low memory\n"); - die(); - } + if (total_size > dos_mem) + die("MEMDISK: Insufficient low memory\n"); driveraddr = stddosmem - total_size; driveraddr &= ~0x3FF; @@ -954,7 +970,7 @@ __cdecl void setup(__cdecl syscall_t cs_syscall, void *cs_bounce) regs.es = 0; regs.eax.b[1] = 0x08; regs.edx.b[0] = geometry->driveno & 0x80; - syscall(0x13, ®s, ®s); + intcall(0x13, ®s, ®s); /* Note: per suggestion from the Interrupt List, consider INT 13 08 to have failed if the sector count in CL is zero. */ @@ -1039,9 +1055,8 @@ __cdecl void setup(__cdecl syscall_t cs_syscall, void *cs_bounce) wrz_8(BIOS_EQUIP, equip); /* Install DPT pointer if this was the only floppy */ - if (getcmditem("dpt") != CMD_NOTFOUND || ((nflop == 1 || no_bpt) - && getcmditem("nodpt") == - CMD_NOTFOUND)) { + if (getcmditem("dpt") != CMD_NOTFOUND || + ((nflop == 1 || no_bpt) && getcmditem("nodpt") == CMD_NOTFOUND)) { /* Do install a replacement DPT into INT 1Eh */ pptr->dpt_ptr = hptr->patch_offs + offsetof(struct patch_area, dpt); } @@ -1059,31 +1074,38 @@ __cdecl void setup(__cdecl syscall_t cs_syscall, void *cs_bounce) printf("new: int13 = %08x int15 = %08x int1e = %08x\n", rdz_32(BIOS_INT13), rdz_32(BIOS_INT15), rdz_32(BIOS_INT1E)); - /* Reboot into the new "disk"; this is also a test for the interrupt hooks */ + /* Figure out entry point */ + if (!boot_seg) { + boot_base = 0x7c00; + shdr->sssp = 0x7c00; + shdr->csip = 0x7c00; + } else { + boot_base = boot_seg << 4; + shdr->sssp = boot_seg << 16; + shdr->csip = boot_seg << 16; + } + + /* Relocate the real-mode code to below the stub */ + rm_base = (driveraddr - rm_args.rm_size) & ~15; + if (rm_base < boot_base + boot_len) + die("MEMDISK: bootstrap too large to load\n"); + + relocate_rm_code(rm_base); + + /* Reboot into the new "disk" */ puts("Loading boot sector... "); - memset(®s, 0, sizeof regs); - // regs.es = 0; - regs.eax.w[0] = 0x0201; /* Read sector */ - regs.ebx.w[0] = 0x7c00; /* 0000:7C00 */ - regs.ecx.w[0] = 1; /* One sector */ - regs.edx.w[0] = geometry->driveno; - syscall(0x13, ®s, ®s); - - if (regs.eflags.l & 1) { - puts("MEMDISK: Failed to load new boot sector\n"); - die(); - } + memcpy((void *)boot_base, (char *)pptr->diskbuf + boot_lba*512, boot_len); if (getcmditem("pause") != CMD_NOTFOUND) { puts("press any key to boot... "); regs.eax.w[0] = 0; - syscall(0x16, ®s, NULL); + intcall(0x16, ®s, NULL); } puts("booting...\n"); /* On return the assembly code will jump to the boot vector */ shdr->esdi = pnp_install_check(); - shdr->edx = geometry->driveno; + shdr->edx = geometry->driveno; } diff --git a/memdisk/start32.S b/memdisk/start32.S index 593ffb3e..4fb05374 100644 --- a/memdisk/start32.S +++ b/memdisk/start32.S @@ -15,6 +15,7 @@ * Simple stub to get us to the right point in the 32-bit code; * this module must be linked first */ + .section ".init", "ax" .globl _start _start: @@ -26,4 +27,75 @@ _start: xorl %eax, %eax shrl $2, %ecx rep ; stosl - jmp setup + + /* Set up the protected-mode IDT and the interrupt jump buffers */ + movl $idt, %edi + movl $ijb, %eax + movl $0xee000000, %ebx /* Interrupt gate */ + movw %cs, %bx /* Target segment */ + + /* Make the IDT */ + movl $256, %ecx +1: + stosl + stosl + movl %ebx, -6(%edi) + addl $8, %eax + loop 1b + + /* + * Each entry in the interrupt jump buffer contains the following + * instructions: + * + * 60 pushal + * b0xx movb $xx, %al # interrupt number + * e9xxxxxxxx jmp handle_interrupt + */ + movl $0xe900b060, %eax + movl $256, %ecx +1: + movl %eax, (%edi) + addl $(1 << 16), %eax + movl $handle_interrupt-8, %edx + subl %edi, %edx + movl %edx, 4(%edi) + addl $8, %edi + loop 1b + + lidtl idt_ptr + + /* Save arguments, switch stacks */ + movl %esp, %eax /* Pointer to arguments */ + movl $__stack_end, %esp + + call setup + jmp *(rm_args) /* First argument is return */ + + .section ".text","ax" + .globl intcall + .type intcall, @function +intcall: + jmp *(rm_args+1*4) /* Intcall is argument 1 */ + .size intcall, .-intcall + + .type handle_interrupt, @function +handle_interrupt: + jmp *(rm_args+4*4) /* Interrupt pointer is argument 4 */ + .size handle_interrupt, .-handle_interrupt + + .section ".rodata","a" +idt_ptr: + .word 8*256-1 + .long idt + .word 0 + + .section ".bss.large","aw" + .balign 2048 +idt: + .space 8*256 +ijb: + .space 8*256 + +__stack: + .space 65536 +__stack_end: diff --git a/memdisk/unzip.c b/memdisk/unzip.c index 9f198a20..9144cf31 100644 --- a/memdisk/unzip.c +++ b/memdisk/unzip.c @@ -144,8 +144,7 @@ static int fill_inbuf(void) { /* This should never happen. We have already pointed the algorithm to all the data we have. */ - printf("failed\nDecompression error: ran out of input data\n"); - die(); + die("failed\nDecompression error: ran out of input data\n"); } /* =========================================================================== @@ -175,8 +174,7 @@ static void flush_window(void) static void error(char *x) { - printf("failed\nDecompression error: %s\n", x); - die(); + die("failed\nDecompression error: %s\n", x); } /* GZIP header */ @@ -349,12 +347,14 @@ int check_zip(void *indata, uint32_t size, uint32_t * zbytes_p, */ extern void _end; +static char heap[65536]; + void *unzip(void *indata, uint32_t zbytes, uint32_t dbytes, uint32_t orig_crc, void *target) { - /* Set up the heap; it's the 64K after the bounce buffer */ - free_mem_ptr = (ulg) sys_bounce + 0x10000; - free_mem_end_ptr = free_mem_ptr + 0x10000; + /* Set up the heap; it is simply a chunk of bss memory */ + free_mem_ptr = (size_t)heap; + free_mem_end_ptr = (size_t)heap + sizeof heap; /* Set up input buffer */ inbuf = indata; |