From be56f8f0420fa5efc55ac9789d41c6307a84bef0 Mon Sep 17 00:00:00 2001
From: "C. Masloch" <pushbx@38.de>
Date: Sun, 27 Jan 2013 16:07:25 +0100
Subject: [PATCH 142/482] 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 <pushbx@38.de>
+
+ 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 <cjwatson@ubuntu.com>
* 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 <grub/video.h>
#include <grub/mm.h>
#include <grub/cpu/relocator.h>
+#include <grub/machine/chainloader.h>
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.2.1