memdisk: relocate real-mode code before booting
authorH. Peter Anvin <hpa@zytor.com>
Mon, 8 Jun 2009 01:25:09 +0000 (18:25 -0700)
committerH. Peter Anvin <hpa@zytor.com>
Mon, 8 Jun 2009 01:25:09 +0000 (18:25 -0700)
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 <hpa@zytor.com>
memdisk/Makefile
memdisk/memdisk.h
memdisk/memdisk16.asm
memdisk/setup.c

index 3bb97ff..e1a8935 100644 (file)
@@ -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
index 41ce13b..5c82c9b 100644 (file)
@@ -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)
index 2568eed..1a22ff6 100644 (file)
@@ -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
index f9b9ed1..a46b12e 100644 (file)
@@ -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(&regs, 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, &regs, &regs);
+    /* 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; 
 }