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