]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Tagged header support
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Wed, 10 Mar 2010 10:40:20 +0000 (11:40 +0100)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Wed, 10 Mar 2010 10:40:20 +0000 (11:40 +0100)
include/grub/multiboot.h
include/multiboot2.h
loader/i386/multiboot.c
loader/i386/multiboot_mbi.c
loader/i386/multiboot_mbi2.c

index 89efbc6dd721333c8d2a54af2a28eeda5c240355..70241ec3cce2ea6e0ee88e43691b19dddae46a47 100644 (file)
@@ -70,7 +70,8 @@ grub_multiboot_fill_vbe_info_real (struct grub_vbe_info_block *vbe_control_info,
 
 grub_err_t
 grub_multiboot_set_console (int console_type, int accepted_consoles,
-                           int width, int height, int depth);
+                           int width, int height, int depth,
+                           int console_required);
 grub_err_t
 grub_multiboot_load (grub_file_t file);
 /* Load ELF32 or ELF64.  */
index dae05a7a311b43710884c7ef36a44371d92e13fa..8204794252ab3ab509d0779b5091efa84fcd8b3f 100644 (file)
@@ -62,6 +62,8 @@
 #define MULTIBOOT_TAG_TYPE_MMAP              6
 #define MULTIBOOT_TAG_TYPE_VBE               7
 #define MULTIBOOT_TAG_TYPE_FRAMEBUFFER       8
+#define MULTIBOOT_TAG_TYPE_ELF_SECTIONS      9
+#define MULTIBOOT_TAG_TYPE_APM               10
 
 #define MULTIBOOT_HEADER_TAG_END  0
 #define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST  1
@@ -74,6 +76,9 @@
 #define GRUB_MULTIBOOT_ARCHITECTURE_I386  0
 #define MULTIBOOT_HEADER_TAG_OPTIONAL 1
 
+#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1
+#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2
+
 #ifndef ASM_FILE
 
 typedef unsigned char          multiboot_uint8_t;
index 17fefc85a8d44ae505772f2963ad899b8c39e9dd..a89233431b5336466f7f9214c0d2d46f63b54673 100644 (file)
@@ -62,6 +62,8 @@ grub_addr_t grub_multiboot_payload_dest;
 grub_size_t grub_multiboot_pure_size;
 grub_uint32_t grub_multiboot_payload_eip;
 static int accepts_video;
+static int accepts_ega_text;
+static int console_required;
 static grub_dl_t my_mod;
 
 
@@ -245,8 +247,23 @@ grub_multiboot_load_elf (grub_file_t file, void *buffer)
 
 grub_err_t
 grub_multiboot_set_console (int console_type, int accepted_consoles,
-                           int width, int height, int depth)
+                           int width, int height, int depth,
+                           int console_req)
 {
+  console_required = console_req;
+  if (!(accepted_consoles 
+       & (GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER
+          | (GRUB_MACHINE_HAS_VGA_TEXT ? GRUB_MULTIBOOT_CONSOLE_EGA_TEXT : 0))))
+    {
+      if (console_required)
+       return grub_error (GRUB_ERR_BAD_OS,
+                          "OS requires a console but none is available");
+      grub_printf ("WARNING: no console will be available to OS");
+      accepts_video = 0;
+      accepts_ega_text = 0;
+      return GRUB_ERR_NONE;
+    }
+
   if (console_type == GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER)
     {
       char *buf;
@@ -267,6 +284,7 @@ grub_multiboot_set_console (int console_type, int accepted_consoles,
    grub_env_set ("gfxpayload", "text");
 
   accepts_video = !!(accepted_consoles & GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER);
+  accepts_ega_text = !!(accepted_consoles & GRUB_MULTIBOOT_CONSOLE_EGA_TEXT);
   return GRUB_ERR_NONE;
 }
 
index e273e29ca17bf3a0b07c9df6c2fc32ed2e43f35a..6d104ed1c4cf78bd6557132881a2b6079c6fae98 100644 (file)
@@ -161,21 +161,21 @@ grub_multiboot_load (grub_file_t file)
          err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_EGA_TEXT, 
                                            GRUB_MULTIBOOT_CONSOLE_EGA_TEXT
                                            | GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER,
-                                           0, 0, 0);
+                                           0, 0, 0, 0);
          break;
        case 0:
          err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER,
                                            GRUB_MULTIBOOT_CONSOLE_EGA_TEXT
                                            | GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER,
                                            header->width, header->height,
-                                           header->depth);
+                                           header->depth, 0);
          break;
        }
     }
   else
     err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_EGA_TEXT, 
                                      GRUB_MULTIBOOT_CONSOLE_EGA_TEXT,
-                                     0, 0, 0);
+                                     0, 0, 0, 0);
   return err;
 }
 
index ab803734f67979f097a761ba4d4e98c789168b6f..d07ce92f3146e03acba1a184ca9512094a43282a 100644 (file)
@@ -23,6 +23,7 @@
 #endif
 #include <grub/multiboot.h>
 #include <grub/cpu/multiboot.h>
+#include <grub/cpu/relocator.h>
 #include <grub/disk.h>
 #include <grub/device.h>
 #include <grub/partition.h>
@@ -59,6 +60,201 @@ static char *cmdline = NULL;
 static int bootdev_set;
 static grub_uint32_t biosdev, slice, part;
 
+grub_err_t
+grub_multiboot_load (grub_file_t file)
+{
+  char *buffer;
+  grub_ssize_t len;
+  struct multiboot_header *header;
+  grub_err_t err;
+  struct multiboot_header_tag *tag;
+  struct multiboot_header_tag_address *addr_tag = NULL;
+  int entry_specified = 0;
+  grub_addr_t entry = 0;
+  grub_uint32_t console_required = 0;
+  struct multiboot_header_tag_framebuffer *fbtag = NULL;
+  int accepted_consoles = GRUB_MULTIBOOT_CONSOLE_EGA_TEXT;
+
+  buffer = grub_malloc (MULTIBOOT_SEARCH);
+  if (!buffer)
+    return grub_errno;
+
+  len = grub_file_read (file, buffer, MULTIBOOT_SEARCH);
+  if (len < 32)
+    {
+      grub_free (buffer);
+      return grub_error (GRUB_ERR_BAD_OS, "file too small");
+    }
+
+  /* Look for the multiboot header in the buffer.  The header should
+     be at least 12 bytes and aligned on a 4-byte boundary.  */
+  for (header = (struct multiboot_header *) buffer;
+       ((char *) header <= buffer + len - 12) || (header = 0);
+       header = (struct multiboot_header *) ((char *) header + MULTIBOOT_HEADER_ALIGN))
+    {
+      if (header->magic == MULTIBOOT_HEADER_MAGIC
+         && !(header->magic + header->architecture
+              + header->header_length + header->checksum))
+       break;
+    }
+
+  if (header == 0)
+    {
+      grub_free (buffer);
+      return grub_error (GRUB_ERR_BAD_ARGUMENT, "no multiboot header found");
+    }
+
+  for (tag = (struct multiboot_header_tag *) (header + 1);
+       tag->type != MULTIBOOT_TAG_TYPE_END;
+       tag = (struct multiboot_header_tag *) ((char *) tag + tag->size))
+    switch (tag->type)
+      {
+      case MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST:
+       {
+         unsigned i;
+         struct multiboot_header_tag_information_request *request_tag
+           = (struct multiboot_header_tag_information_request *) tag;
+         if (request_tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL)
+           break;
+         for (i = 0; i < (request_tag->size - sizeof (request_tag))
+                / sizeof (request_tag->requests[0]); i++)
+           switch (request_tag->requests[i])
+             {
+             case MULTIBOOT_TAG_TYPE_END:
+             case MULTIBOOT_TAG_TYPE_CMDLINE:
+             case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME:
+             case MULTIBOOT_TAG_TYPE_MODULE:
+             case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO:
+             case MULTIBOOT_TAG_TYPE_BOOTDEV:
+             case MULTIBOOT_TAG_TYPE_MMAP:
+             case MULTIBOOT_TAG_TYPE_VBE:
+             case MULTIBOOT_TAG_TYPE_FRAMEBUFFER:
+               break;
+
+             case MULTIBOOT_TAG_TYPE_ELF_SECTIONS:
+             case MULTIBOOT_TAG_TYPE_APM:
+             default:
+               grub_free (buffer);
+               return grub_error (GRUB_ERR_UNKNOWN_OS,
+                                  "unsupported information tag: 0x%x",
+                                  request_tag->requests[i]);
+             }
+         break;
+       }
+              
+      case MULTIBOOT_HEADER_TAG_ADDRESS:
+       addr_tag = (struct multiboot_header_tag_address *) tag;
+       break;
+
+      case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS:
+       entry_specified = 1;
+       entry = ((struct multiboot_header_tag_entry_address *) tag)->entry_addr;
+       break;
+
+      case MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS:
+       if (!(((struct multiboot_header_tag_console_flags *) tag)->console_flags
+           & MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED))
+         accepted_consoles &= ~GRUB_MULTIBOOT_CONSOLE_EGA_TEXT;
+       if (((struct multiboot_header_tag_console_flags *) tag)->console_flags
+           & MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED)
+         console_required = 1;
+       break;
+
+      case MULTIBOOT_HEADER_TAG_FRAMEBUFFER:
+       fbtag = (struct multiboot_header_tag_framebuffer *) tag;
+       accepted_consoles |= GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER;
+       break;
+
+       /* GRUB always page-aligns modules.  */
+      case MULTIBOOT_HEADER_TAG_MODULE_ALIGN:
+       break;
+
+      default:
+       if (! (tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL))
+         {
+           grub_free (buffer);
+           return grub_error (GRUB_ERR_UNKNOWN_OS,
+                              "unsupported tag: 0x%x", tag->type);
+         }
+       break;
+      }
+
+  if (addr_tag && !entry_specified)
+    {
+      grub_free (buffer);
+      return grub_error (GRUB_ERR_UNKNOWN_OS,
+                        "load address tag without entry address tag");
+    }
+  if (addr_tag)
+    {
+      int offset = ((char *) header - buffer -
+                   (addr_tag->header_addr - addr_tag->load_addr));
+      int load_size = ((addr_tag->load_end_addr == 0) ? file->size - offset :
+                      addr_tag->load_end_addr - addr_tag->load_addr);
+      grub_size_t code_size;
+
+      if (addr_tag->bss_end_addr)
+       code_size = (addr_tag->bss_end_addr - addr_tag->load_addr);
+      else
+       code_size = load_size;
+      grub_multiboot_payload_dest = addr_tag->load_addr;
+
+      grub_multiboot_pure_size += code_size;
+
+      /* Allocate a bit more to avoid relocations in most cases.  */
+      grub_multiboot_alloc_mbi = grub_multiboot_get_mbi_size () + 65536;
+      grub_multiboot_payload_orig
+       = grub_relocator32_alloc (grub_multiboot_pure_size + grub_multiboot_alloc_mbi);
+
+      if (! grub_multiboot_payload_orig)
+       {
+         grub_free (buffer);
+         return grub_errno;
+       }
+
+      if ((grub_file_seek (file, offset)) == (grub_off_t) -1)
+       {
+         grub_free (buffer);
+         return grub_errno;
+       }
+
+      grub_file_read (file, (void *) grub_multiboot_payload_orig, load_size);
+      if (grub_errno)
+       {
+         grub_free (buffer);
+         return grub_errno;
+       }
+
+      if (addr_tag->bss_end_addr)
+       grub_memset ((void *) (grub_multiboot_payload_orig + load_size), 0,
+                    addr_tag->bss_end_addr - addr_tag->load_addr - load_size);
+    }
+  else
+    {
+      err = grub_multiboot_load_elf (file, buffer);
+      if (err)
+       {
+         grub_free (buffer);
+         return err;
+       }
+    }
+
+  if (entry_specified)
+    grub_multiboot_payload_eip = entry;
+
+  if (fbtag)
+    err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER,
+                                     accepted_consoles,
+                                     fbtag->width, fbtag->height,
+                                     fbtag->depth, console_required);
+  else
+    err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_EGA_TEXT,
+                                     accepted_consoles,
+                                     0, 0, 0, console_required);
+  return err;
+}
+
 grub_size_t
 grub_multiboot_get_mbi_size (void)
 {