From 29dea87ef5ea27bd94eb6aa880cfc0a9ee4aa308 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 7 Jun 2009 18:25:09 -0700 Subject: memdisk: relocate real-mode code before booting Relocate the real-mode code before booting. This allows the target bootstrap to be loaded at an arbitrary address, not necessarily 0x7c00, and to be almost arbitrarily long. Add some initial infrastructure for other bootstrap addresses, too. Signed-off-by: H. Peter Anvin --- memdisk/Makefile | 4 +- memdisk/memdisk.h | 6 ++- memdisk/memdisk16.asm | 34 ++++++++++----- memdisk/setup.c | 114 +++++++++++++++++++++++++++++++++++++------------- 4 files changed, 115 insertions(+), 43 deletions(-) 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/memdisk.h b/memdisk/memdisk.h index 41ce13b1..5c82c9b8 100644 --- a/memdisk/memdisk.h +++ b/memdisk/memdisk.h @@ -34,8 +34,10 @@ extern void *sys_bounce; extern void __attribute__ ((noreturn)) die(void); /* 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) diff --git a/memdisk/memdisk16.asm b/memdisk/memdisk16.asm index 2568eed4..1a22ff6f 100644 --- a/memdisk/memdisk16.asm +++ b/memdisk/memdisk16.asm @@ -170,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 @@ -183,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 @@ -209,7 +212,7 @@ 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 @@ -563,6 +566,7 @@ call32_enter_pm: mov [.pm_jmp+2],eax ; Patch the PM jump jmp .sync .sync: + 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 @@ -576,12 +580,16 @@ call32_enter_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 al,08h + ltr ax + mov esp,[ebp+PMESP] ; Load protmode %esp if available jmp ebx ; Go to where we need to go @@ -597,6 +605,7 @@ call32_call_start: mov esp, (BOUNCE_SEG << 4) + 0x10000 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 @@ -628,11 +637,11 @@ call32_enter_rm: cld 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 @@ -755,8 +764,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 diff --git a/memdisk/setup.c b/memdisk/setup.c index f9b9ed1f..a46b12e6 100644 --- a/memdisk/setup.c +++ b/memdisk/setup.c @@ -163,10 +163,14 @@ struct real_mode_args { uint32_t rm_bounce; uint32_t rm_base; uint32_t rm_handle_interrupt; + uint32_t rm_gdt; uint32_t rm_size; }; struct real_mode_args rm_args; +__cdecl syscall_t syscall; +void *sys_bounce; + /* Access to high memory */ /* Access to objects in the zero page */ @@ -348,9 +352,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; } @@ -670,9 +672,9 @@ const struct geometry *get_disk_image_geometry(uint32_t where, uint32_t size) */ void __attribute__ ((noreturn)) die(void) { - asm volatile ("sti"); + sti(); for (;;) - asm volatile ("hlt"); + asm volatile("hlt"); } /* @@ -703,6 +705,58 @@ static uint32_t pnp_install_check(void) return 0; } +static void update_global_vars(void) +{ + syscall = (__cdecl syscall_t) rm_args.rm_syscall; + sys_bounce = (void *)rm_args.rm_bounce; + shdr = (void *)rm_args.rm_base; +} + +/* + * 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_syscall += delta; + rm_args.rm_bounce += delta; + rm_args.rm_base += delta; + rm_args.rm_gdt += delta; + rm_args.rm_handle_interrupt += 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)); + sti(); + + update_global_vars(); +} + #define STACK_NEEDED 512 /* Number of bytes of stack */ /* @@ -710,9 +764,6 @@ static uint32_t pnp_install_check(void) * Returns the drive number (which is then passed in %dl to the * called routine.) */ -__cdecl syscall_t syscall; -void *sys_bounce; - void setup(const struct real_mode_args *rm_args_ptr) { unsigned int bin_size; @@ -727,18 +778,20 @@ void setup(const struct real_mode_args *rm_args_ptr) 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 */ /* 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 */ /* Set up global variables */ - syscall = (__cdecl syscall_t) rm_args.rm_syscall; - sys_bounce = (void *)rm_args.rm_bounce; - shdr = (void *)rm_args.rm_base; + update_global_vars(); /* Show signs of life */ printf("%s %s\n", memdisk_version, copyright); @@ -1056,9 +1109,8 @@ void setup(const struct real_mode_args *rm_args_ptr) 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); } @@ -1076,21 +1128,29 @@ void setup(const struct real_mode_args *rm_args_ptr) 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 */ - 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); + /* 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; + } - if (regs.eflags.l & 1) { - puts("MEMDISK: Failed to load new boot sector\n"); + /* Relocate the real-mode code to below the stub */ + rm_base = (driveraddr - rm_args.rm_size) & ~15; + if (rm_base < boot_base + boot_len) { + puts("MEMDISK: bootstrap too large to load\n"); die(); } + relocate_rm_code(rm_base); + + /* Reboot into the new "disk" */ + puts("Loading boot sector... "); + + memcpy((void *)boot_base, (char *)pptr->diskbuf + boot_lba*512, boot_len); if (getcmditem("pause") != CMD_NOTFOUND) { puts("press any key to boot... "); @@ -1103,6 +1163,4 @@ void setup(const struct real_mode_args *rm_args_ptr) /* On return the assembly code will jump to the boot vector */ shdr->esdi = pnp_install_check(); shdr->edx = geometry->driveno; - shdr->sssp = 0x7c00; - shdr->csip = 0x7c00; } -- cgit v1.2.1