From cf10c476b8dbe718f05da15a705ba106eae9f621 Mon Sep 17 00:00:00 2001 From: "C. Masloch" Date: Sun, 27 Jan 2013 16:07:25 +0100 Subject: [PATCH 141/364] Improve FreeDOS direct loading support compatibility. * include/grub/i386/relocator.h (grub_relocator16_state): New member ebp. * grub-core/lib/i386/relocator.c (grub_relocator16_ebp): New extern variable. (grub_relocator16_boot): Handle %ebp. * grub-core/lib/i386/relocator16.S: Likewise. * grub-core/loader/i386/pc/freedos.c: Load BPB to pass kernel which partition to load from. Check that kernel file is not too large. Set register dl to BIOS unit number as well. --- ChangeLog | 15 ++++++++++ grub-core/lib/i386/relocator.c | 2 ++ grub-core/lib/i386/relocator16.S | 5 ++++ grub-core/loader/i386/pc/freedos.c | 61 ++++++++++++++++++++++++++++++++++---- include/grub/i386/relocator.h | 1 + 5 files changed, 79 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8c4d087..f5cb7dc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2013-01-27 C. Masloch + + Improve FreeDOS direct loading support compatibility. + + * include/grub/i386/relocator.h (grub_relocator16_state): + New member ebp. + * grub-core/lib/i386/relocator.c (grub_relocator16_ebp): New extern + variable. + (grub_relocator16_boot): Handle %ebp. + * grub-core/lib/i386/relocator16.S: Likewise. + * grub-core/loader/i386/pc/freedos.c: + Load BPB to pass kernel which partition to load from. + Check that kernel file is not too large. + Set register dl to BIOS unit number as well. + 2013-01-22 Colin Watson * util/grub-reboot.in (usage): Document the need for diff --git a/grub-core/lib/i386/relocator.c b/grub-core/lib/i386/relocator.c index df25b30..0170eed 100644 --- a/grub-core/lib/i386/relocator.c +++ b/grub-core/lib/i386/relocator.c @@ -54,6 +54,7 @@ extern grub_uint16_t grub_relocator16_sp; extern grub_uint32_t grub_relocator16_edx; extern grub_uint32_t grub_relocator16_ebx; extern grub_uint32_t grub_relocator16_esi; +extern grub_uint32_t grub_relocator16_ebp; extern grub_uint16_t grub_relocator16_keep_a20_enabled; @@ -225,6 +226,7 @@ grub_relocator16_boot (struct grub_relocator *rel, grub_relocator16_ss = state.ss; grub_relocator16_sp = state.sp; + grub_relocator16_ebp = state.ebp; grub_relocator16_ebx = state.ebx; grub_relocator16_edx = state.edx; grub_relocator16_esi = state.esi; diff --git a/grub-core/lib/i386/relocator16.S b/grub-core/lib/i386/relocator16.S index e79d875..c8d6f86 100644 --- a/grub-core/lib/i386/relocator16.S +++ b/grub-core/lib/i386/relocator16.S @@ -259,6 +259,11 @@ VARIABLE(grub_relocator16_edx) VARIABLE(grub_relocator16_ebx) .long 0 + /* movl imm32, %ebp. */ + .byte 0x66, 0xbd +VARIABLE(grub_relocator16_ebp) + .long 0 + /* Cleared direction flag is of no problem with any current payload and makes this implementation easier. */ cld diff --git a/grub-core/loader/i386/pc/freedos.c b/grub-core/loader/i386/pc/freedos.c index f1eed57..e685c6e 100644 --- a/grub-core/loader/i386/pc/freedos.c +++ b/grub-core/loader/i386/pc/freedos.c @@ -32,6 +32,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -40,8 +41,23 @@ static struct grub_relocator *rel; static grub_uint32_t ebx = 0xffffffff; #define GRUB_FREEDOS_SEGMENT 0x60 +#define GRUB_FREEDOS_ADDR (GRUB_FREEDOS_SEGMENT << 4) #define GRUB_FREEDOS_STACK_SEGMENT 0x1fe0 -#define GRUB_FREEDOS_STACK_POINTER 0x8000 +#define GRUB_FREEDOS_STACK_BPB_POINTER 0x7c00 +#define GRUB_FREEDOS_BPB_ADDR ((GRUB_FREEDOS_STACK_SEGMENT << 4) \ + + GRUB_FREEDOS_STACK_BPB_POINTER) + +/* FreeDOS boot.asm passes register sp as exactly this. Importantly, + it must point below the BPB (to avoid overwriting any of it). */ +#define GRUB_FREEDOS_STACK_POINTER (GRUB_FREEDOS_STACK_BPB_POINTER \ + - 0x60) + +/* In this, the additional 8192 bytes are the stack reservation; the + remaining parts trivially give the maximum allowed size. */ +#define GRUB_FREEDOS_MAX_SIZE ((GRUB_FREEDOS_STACK_SEGMENT << 4) \ + + GRUB_FREEDOS_STACK_POINTER \ + - GRUB_FREEDOS_ADDR \ + - 8192) static grub_err_t grub_freedos_boot (void) @@ -49,14 +65,16 @@ grub_freedos_boot (void) struct grub_relocator16_state state = { .cs = GRUB_FREEDOS_SEGMENT, .ip = 0, - .ds = 0, + + .ds = GRUB_FREEDOS_STACK_SEGMENT, .es = 0, .fs = 0, .gs = 0, .ss = GRUB_FREEDOS_STACK_SEGMENT, .sp = GRUB_FREEDOS_STACK_POINTER, + .ebp = GRUB_FREEDOS_STACK_BPB_POINTER, .ebx = ebx, - .edx = 0, + .edx = ebx, .a20 = 1 }; grub_video_set_mode ("text", 0, 0); @@ -79,8 +97,9 @@ grub_cmd_freedos (grub_command_t cmd __attribute__ ((unused)), { grub_file_t file = 0; grub_err_t err; - void *kernelsys; + void *bs, *kernelsys; grub_size_t kernelsyssize; + grub_device_t dev; if (argc == 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); @@ -95,12 +114,44 @@ grub_cmd_freedos (grub_command_t cmd __attribute__ ((unused)), if (! file) goto fail; + { + grub_relocator_chunk_t ch; + err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_FREEDOS_BPB_ADDR, + GRUB_DISK_SECTOR_SIZE); + if (err) + goto fail; + bs = get_virtual_current_address (ch); + } + ebx = grub_get_root_biosnumber (); + dev = grub_device_open (0); + + if (dev && dev->disk) + { + err = grub_disk_read (dev->disk, 0, 0, GRUB_DISK_SECTOR_SIZE, bs); + if (err) + { + grub_device_close (dev); + goto fail; + } + grub_chainloader_patch_bpb (bs, dev, ebx); + } + + if (dev) + grub_device_close (dev); kernelsyssize = grub_file_size (file); + + if (kernelsyssize > GRUB_FREEDOS_MAX_SIZE) + { + grub_error (GRUB_ERR_BAD_OS, + N_("file `%s' is too large"), argv[0]); + goto fail; + } + { grub_relocator_chunk_t ch; - err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_FREEDOS_SEGMENT << 4, + err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_FREEDOS_ADDR, kernelsyssize); if (err) goto fail; diff --git a/include/grub/i386/relocator.h b/include/grub/i386/relocator.h index 46becb8..5f89a7e 100644 --- a/include/grub/i386/relocator.h +++ b/include/grub/i386/relocator.h @@ -49,6 +49,7 @@ struct grub_relocator16_state grub_uint32_t ebx; grub_uint32_t edx; grub_uint32_t esi; + grub_uint32_t ebp; int a20; }; -- 1.8.1.4