aboutsummaryrefslogtreecommitdiffstats
path: root/memdisk
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2009-06-07 18:25:09 -0700
committerH. Peter Anvin <hpa@zytor.com>2009-06-07 18:25:09 -0700
commit29dea87ef5ea27bd94eb6aa880cfc0a9ee4aa308 (patch)
tree2b66cf11e523114dd2564211cef22f7bc7421ed8 /memdisk
parent788de9d8dc339c5fe99029131eb6e2c152181c6a (diff)
downloadsyslinux.git-29dea87ef5ea27bd94eb6aa880cfc0a9ee4aa308.tar.gz
syslinux.git-29dea87ef5ea27bd94eb6aa880cfc0a9ee4aa308.tar.xz
syslinux.git-29dea87ef5ea27bd94eb6aa880cfc0a9ee4aa308.zip
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 <hpa@zytor.com>
Diffstat (limited to 'memdisk')
-rw-r--r--memdisk/Makefile4
-rw-r--r--memdisk/memdisk.h6
-rw-r--r--memdisk/memdisk16.asm34
-rw-r--r--memdisk/setup.c114
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(&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;
}