]> git.proxmox.com Git - grub2.git/commitdiff
multiboot video support
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Thu, 14 Jan 2010 14:54:14 +0000 (15:54 +0100)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Thu, 14 Jan 2010 14:54:14 +0000 (15:54 +0100)
ChangeLog.mbivid [new file with mode: 0644]
include/grub/multiboot.h
include/grub/video.h
include/multiboot.h
include/multiboot2.h
loader/i386/multiboot.c
loader/i386/multiboot_mbi.c
video/efi_gop.c
video/efi_uga.c
video/i386/pc/vbe.c
video/video.c

diff --git a/ChangeLog.mbivid b/ChangeLog.mbivid
new file mode 100644 (file)
index 0000000..da4fa3c
--- /dev/null
@@ -0,0 +1,24 @@
+2010-01-14  Vladimir Serbinenko  <phcoder@gmail.com>
+
+       Video multiboot support.
+
+       * include/grub/multiboot.h (grub_multiboot_set_accepts_video):
+       New prototype.
+       * include/grub/video.h (grub_video_driver_id): New type.
+       (grub_video_adapter): New member 'id'. All users updated.
+       (grub_video_get_driver_id): New proto.
+       * include/multiboot.h: Resynced with multiboot specification.
+       * include/multiboot2.h: Likewise.
+       * loader/i386/multiboot.c (UNSUPPORTED_FLAGS): Support video flags.
+       (grub_multiboot): Parse MULTIBOOT_VIDEO_MODE fields.
+       * loader/i386/multiboot_mbi.c (DEFAULT_VIDEO_MODE): New constant.
+       (HAS_VGA_TEXT): Likewise.
+       (HAS_VBE): Likewise.
+       (accepts_video): New variable.
+       (grub_multiboot_set_accepts_video): New function.
+       (grub_multiboot_get_mbi_size): Account for video structures.
+       (set_video_mode): New function.
+       (fill_vbe_info) [HAS_VBE]: Likewise.
+       (retrieve_video_parameters): Likewise.
+       (grub_multiboot_make_mbi): Fill video fields.
+       * video/video.c (grub_video_get_driver_id): New function.
index f9edb0c598b84619b7d5c178ad3da583b6352feb..665292e330425267bfcb1ee1ca2579f67a18b62a 100644 (file)
@@ -35,6 +35,8 @@
 void grub_multiboot (int argc, char *argv[]);
 void grub_module (int argc, char *argv[]);
 
+void grub_multiboot_set_accepts_video (int val);
+
 grub_size_t grub_multiboot_get_mbi_size (void);
 grub_err_t grub_multiboot_make_mbi (void *orig, grub_uint32_t dest,
                                    grub_off_t buf_off, grub_size_t bufsize);
index 4145db46516172a84fbf25c3ac05153535a40a0f..437c98eb9bfde36eafde0d6c227583c950197948 100644 (file)
@@ -159,10 +159,19 @@ struct grub_video_palette_data
   grub_uint8_t a; /* Reserved bits value (0-255).  */
 };
 
+typedef enum grub_video_driver_id
+  {
+    GRUB_VIDEO_DRIVER_NONE,
+    GRUB_VIDEO_DRIVER_VBE,
+    GRUB_VIDEO_DRIVER_EFI_UGA,
+    GRUB_VIDEO_DRIVER_EFI_GOP
+  } grub_video_driver_id_t;
+
 struct grub_video_adapter
 {
   /* The video adapter name.  */
   const char *name;
+  grub_video_driver_id_t id;
 
   /* Initialize the video adapter.  */
   grub_err_t (*init) (void);
@@ -310,4 +319,7 @@ grub_err_t grub_video_set_mode (const char *modestring,
                                int NESTED_FUNC_ATTR (*hook) (grub_video_adapter_t p,
                                                              struct grub_video_mode_info *mode_info));
 
+grub_video_driver_id_t
+grub_video_get_driver_id (void);
+
 #endif /* ! GRUB_VIDEO_HEADER */
index da7afd9b3b6d93b731795025919cb544eac84342..460347d22d6dcad29adc2580ad65e54f454f9d42 100644 (file)
 #define MULTIBOOT_INFO_APM_TABLE               0x00000400
 
 /* Is there video information?  */
-#define MULTIBOOT_INFO_VIDEO_INFO              0x00000800
+#define MULTIBOOT_INFO_VBE_INFO                        0x00000800
+#define MULTIBOOT_INFO_FRAMEBUFFER_INFO                0x00001000
 
 #ifndef ASM_FILE
 
+typedef unsigned char          multiboot_uint8_t;
 typedef unsigned short         multiboot_uint16_t;
 typedef unsigned int           multiboot_uint32_t;
 typedef unsigned long long     multiboot_uint64_t;
@@ -187,9 +189,43 @@ struct multiboot_info
   multiboot_uint16_t vbe_interface_seg;
   multiboot_uint16_t vbe_interface_off;
   multiboot_uint16_t vbe_interface_len;
+
+  multiboot_uint64_t framebuffer_addr;
+  multiboot_uint32_t framebuffer_pitch;
+  multiboot_uint32_t framebuffer_width;
+  multiboot_uint32_t framebuffer_height;
+  multiboot_uint8_t framebuffer_bpp;
+  multiboot_uint8_t framebuffer_type;
+  union
+  {
+    struct
+    {
+      multiboot_uint32_t framebuffer_palette_addr;
+      multiboot_uint16_t framebuffer_palette_num_colors;
+    };
+    struct
+    {
+      multiboot_uint8_t framebuffer_red_field_position;
+      multiboot_uint8_t framebuffer_red_mask_size;
+      multiboot_uint8_t framebuffer_green_field_position;
+      multiboot_uint8_t framebuffer_green_mask_size;
+      multiboot_uint8_t framebuffer_blue_field_position;
+      multiboot_uint8_t framebuffer_blue_mask_size;
+    };
+  };
 };
 typedef struct multiboot_info multiboot_info_t;
 
+struct multiboot_color
+{
+  multiboot_uint8_t red;
+  multiboot_uint8_t green;
+  multiboot_uint8_t blue;
+};
+
+#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0
+#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB     1
+
 struct multiboot_mmap_entry
 {
   multiboot_uint32_t size;
index 0488849d7f730a3110f4244351f98df22ab51018..8bcf5a7e48e294b1a43b157d3d23569f49708c37 100644 (file)
 #define MULTIBOOT_INFO_APM_TABLE               0x00000400
 
 /* Is there video information?  */
-#define MULTIBOOT_INFO_VIDEO_INFO              0x00000800
+#define MULTIBOOT_INFO_VBE_INFO                        0x00000800
+#define MULTIBOOT_INFO_FRAMEBUFFER_INFO                0x00001000
 
 #ifndef ASM_FILE
 
+typedef unsigned char          multiboot_uint8_t;
 typedef unsigned short         multiboot_uint16_t;
 typedef unsigned int           multiboot_uint32_t;
 typedef unsigned long long     multiboot_uint64_t;
@@ -187,9 +189,43 @@ struct multiboot_info
   multiboot_uint16_t vbe_interface_seg;
   multiboot_uint16_t vbe_interface_off;
   multiboot_uint16_t vbe_interface_len;
+
+  multiboot_uint64_t framebuffer_addr;
+  multiboot_uint32_t framebuffer_pitch;
+  multiboot_uint32_t framebuffer_width;
+  multiboot_uint32_t framebuffer_height;
+  multiboot_uint8_t framebuffer_bpp;
+  multiboot_uint8_t framebuffer_type;
+  union
+  {
+    struct
+    {
+      multiboot_uint32_t framebuffer_palette_addr;
+      multiboot_uint16_t framebuffer_palette_num_colors;
+    };
+    struct
+    {
+      multiboot_uint8_t framebuffer_red_field_position;
+      multiboot_uint8_t framebuffer_red_mask_size;
+      multiboot_uint8_t framebuffer_green_field_position;
+      multiboot_uint8_t framebuffer_green_mask_size;
+      multiboot_uint8_t framebuffer_blue_field_position;
+      multiboot_uint8_t framebuffer_blue_mask_size;
+    };
+  };
 };
 typedef struct multiboot_info multiboot_info_t;
 
+struct multiboot_color
+{
+  multiboot_uint8_t red;
+  multiboot_uint8_t green;
+  multiboot_uint8_t blue;
+};
+
+#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0
+#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB     1
+
 struct multiboot_mmap_entry
 {
   multiboot_uint32_t size;
index 5dc30411777868209c962a33a3425d0b2c1c0aa0..15f9977c9f2e593018399c5389d1ee99cd6a06cb 100644 (file)
@@ -20,7 +20,6 @@
 /*
  *  FIXME: The following features from the Multiboot specification still
  *         need to be implemented:
- *  - VBE support
  *  - symbol table
  *  - drives table
  *  - ROM configuration table
  */
 
 /* The bits in the required part of flags field we don't support.  */
-#define UNSUPPORTED_FLAGS                      0x0000fffc
+#define UNSUPPORTED_FLAGS                      0x0000fff8
 
 #include <grub/loader.h>
 #include <grub/machine/loader.h>
 #include <grub/multiboot.h>
-#include <grub/machine/init.h>
-#include <grub/machine/memory.h>
 #include <grub/cpu/multiboot.h>
 #include <grub/elf.h>
 #include <grub/aout.h>
@@ -232,6 +229,34 @@ grub_multiboot (int argc, char *argv[])
   else if (grub_multiboot_load_elf (file, buffer) != GRUB_ERR_NONE)
     goto fail;
 
+  if (header->flags & MULTIBOOT_VIDEO_MODE)
+    {
+      switch (header->mode_type)
+       {
+       case 1:
+         grub_env_set ("gfxpayload", "text");
+         break;
+
+       case 0:
+         {
+           char buf[sizeof ("XXXXXXXXXXxXXXXXXXXXXxXXXXXXXXXX,XXXXXXXXXXxXXXXXXXXXX,auto")];
+           if (header->depth && header->width && header->height)
+             grub_sprintf (buf, "%dx%dx%d,%dx%d,auto", header->width,
+                           header->height, header->depth, header->width,
+                           header->height);
+           else if (header->width && header->height)
+             grub_sprintf (buf, "%dx%d,auto", header->width, header->height);
+           else
+             grub_sprintf (buf, "auto");
+
+           grub_env_set ("gfxpayload", buf);
+           break;
+         }
+       }
+    }
+
+  grub_multiboot_set_accepts_video (!!(header->flags & MULTIBOOT_VIDEO_MODE));
+
   grub_multiboot_set_bootdev ();
 
   grub_loader_set (grub_multiboot_boot, grub_multiboot_unload, 0);
index 58802d44c95c941cfdf51bc0725cf28b0fdf4e44..f362b800db802ea9b3611a961013fce5b3c6488e 100644 (file)
 #include <grub/mm.h>
 #include <grub/misc.h>
 #include <grub/env.h>
+#include <grub/video.h>
+
+#if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU)
+#include <grub/i386/pc/vbe.h>
+#define DEFAULT_VIDEO_MODE "text"
+#define HAS_VGA_TEXT 1
+#define HAS_VBE 1
+#else
+#define DEFAULT_VIDEO_MODE "auto"
+#define HAS_VGA_TEXT 0
+#define HAS_VBE 0
+#endif
 
 struct module
 {
@@ -46,6 +58,13 @@ static unsigned modcnt;
 static char *cmdline = NULL;
 static grub_uint32_t bootdev;
 static int bootdev_set;
+static int accepts_video;
+
+void
+grub_multiboot_set_accepts_video (int val)
+{
+  accepts_video = val;
+}
 
 /* Return the length of the Multiboot mmap that will be needed to allocate
    our platform's map.  */
@@ -73,7 +92,12 @@ grub_multiboot_get_mbi_size (void)
 {
   return sizeof (struct multiboot_info) + ALIGN_UP (cmdline_size, 4)
     + modcnt * sizeof (struct multiboot_mod_list) + total_modcmd
-    + ALIGN_UP (sizeof(PACKAGE_STRING), 4) + grub_get_multiboot_mmap_len ();
+    + ALIGN_UP (sizeof(PACKAGE_STRING), 4) + grub_get_multiboot_mmap_len ()
+#if HAS_VBE
+    + sizeof (struct grub_vbe_info_block)
+    + sizeof (struct grub_vbe_mode_info_block)
+#endif
+    + 256 * sizeof (struct multiboot_color);
 }
 
 /* Fill previously allocated Multiboot mmap.  */
@@ -106,6 +130,160 @@ grub_fill_multiboot_mmap (struct multiboot_mmap_entry *first_entry)
   grub_mmap_iterate (hook);
 }
 
+static grub_err_t
+set_video_mode (void)
+{
+  grub_err_t err;
+  const char *modevar;
+
+  if (accepts_video || !HAS_VGA_TEXT)
+    {
+      modevar = grub_env_get ("gfxpayload");
+      if (! modevar || *modevar == 0)
+       err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0);
+      else
+       {
+         char *tmp;
+         tmp = grub_malloc (grub_strlen (modevar)
+                            + sizeof (DEFAULT_VIDEO_MODE) + 1);
+         if (! tmp)
+           return grub_errno;
+         grub_sprintf (tmp, "%s;" DEFAULT_VIDEO_MODE, modevar);
+         err = grub_video_set_mode (tmp, 0);
+         grub_free (tmp);
+       }
+    }
+  else
+    err = grub_video_set_mode ("text", 0);
+
+  return err;
+}
+
+#if HAS_VBE
+static grub_err_t
+fill_vbe_info (struct multiboot_info *mbi, grub_uint8_t *ptrorig,
+              grub_uint32_t ptrdest)
+{
+  grub_vbe_status_t status;
+  grub_uint32_t vbe_mode;
+  void *scratch = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
+    
+  status = grub_vbe_bios_get_controller_info (scratch);
+  if (status != GRUB_VBE_STATUS_OK)
+    return grub_error (GRUB_ERR_IO, "Can't get controller info.");
+  
+  mbi->vbe_control_info = ptrdest;
+  grub_memcpy (ptrorig, scratch, sizeof (struct grub_vbe_info_block));
+  ptrorig += sizeof (struct grub_vbe_info_block);
+  ptrdest += sizeof (struct grub_vbe_info_block);
+  
+  status = grub_vbe_bios_get_mode (scratch);
+  vbe_mode = *(grub_uint32_t *) scratch;
+  if (status != GRUB_VBE_STATUS_OK)
+    return grub_error (GRUB_ERR_IO, "Can't get VBE mode.");
+  mbi->vbe_mode = vbe_mode;
+
+  status = grub_vbe_bios_get_mode_info (vbe_mode, scratch);
+  if (status != GRUB_VBE_STATUS_OK)
+    return grub_error (GRUB_ERR_IO, "Can't get mode info.");
+  mbi->vbe_mode_info = ptrdest;
+  grub_memcpy (ptrorig, scratch, sizeof (struct grub_vbe_mode_info_block));
+  ptrorig += sizeof (struct grub_vbe_mode_info_block);
+  ptrdest += sizeof (struct grub_vbe_mode_info_block);
+      
+  /* FIXME: retrieve those.  */
+  mbi->vbe_interface_seg = 0;
+  mbi->vbe_interface_off = 0;
+  mbi->vbe_interface_len = 0;
+  
+  mbi->flags |= MULTIBOOT_INFO_VBE_INFO;
+
+  return GRUB_ERR_NONE;
+}
+#endif
+
+static grub_err_t
+retrieve_video_parameters (struct multiboot_info *mbi,
+                          grub_uint8_t *ptrorig, grub_uint32_t ptrdest)
+{
+  grub_err_t err;
+  struct grub_video_mode_info mode_info;
+  void *framebuffer;
+#if HAS_VBE
+  int vbe_active;
+#endif
+  struct grub_video_palette_data palette[256];
+
+  err = set_video_mode ();
+  if (err)
+    return err;
+
+  grub_video_get_palette (0, ARRAY_SIZE (palette), palette);
+
+#if HAS_VBE
+  {
+    grub_video_driver_id_t driv_id;
+    driv_id = grub_video_get_driver_id ();
+  
+    vbe_active = ((driv_id == GRUB_VIDEO_DRIVER_VBE)
+                 || ((driv_id == GRUB_VIDEO_DRIVER_NONE) && HAS_VGA_TEXT));
+  }
+#endif
+
+  err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
+  if (err)
+    return err;
+
+  mbi->framebuffer_addr = (grub_addr_t) framebuffer;
+  mbi->framebuffer_pitch = mode_info.pitch;
+
+  mbi->framebuffer_width = mode_info.width;
+  mbi->framebuffer_height = mode_info.height;
+
+  mbi->framebuffer_bpp = mode_info.bpp;
+      
+  if (mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR)
+    {
+      struct multiboot_color *mb_palette;
+      unsigned i;
+      mbi->framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED;
+      mbi->framebuffer_palette_addr = ptrdest;
+      mbi->framebuffer_palette_num_colors = mode_info.number_of_colors;
+      if (mbi->framebuffer_palette_num_colors > ARRAY_SIZE (palette))
+       mbi->framebuffer_palette_num_colors = ARRAY_SIZE (palette);
+      mb_palette = (struct multiboot_color *) ptrorig;
+      for (i = 0; i < mbi->framebuffer_palette_num_colors; i++)
+       {
+         mb_palette[i].red = palette[i].r;
+         mb_palette[i].green = palette[i].g;
+         mb_palette[i].blue = palette[i].b;
+       }
+      ptrorig += mbi->framebuffer_palette_num_colors
+       * sizeof (struct multiboot_color);
+      ptrdest += mbi->framebuffer_palette_num_colors
+       * sizeof (struct multiboot_color);
+    }
+  else
+    {
+      mbi->framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_RGB;
+      mbi->framebuffer_red_field_position = mode_info.green_field_pos;
+      mbi->framebuffer_red_mask_size = mode_info.green_mask_size;
+      mbi->framebuffer_green_field_position = mode_info.green_field_pos;
+      mbi->framebuffer_green_mask_size = mode_info.green_mask_size;
+      mbi->framebuffer_blue_field_position = mode_info.blue_field_pos;
+      mbi->framebuffer_blue_mask_size = mode_info.blue_mask_size;
+    }
+
+  mbi->flags |= MULTIBOOT_INFO_FRAMEBUFFER_INFO;
+
+#if HAS_VBE
+  if (vbe_active)
+    fill_vbe_info (mbi, ptrorig, ptrdest);
+#endif
+
+  return GRUB_ERR_NONE;
+}
+
 grub_err_t
 grub_multiboot_make_mbi (void *orig, grub_uint32_t dest, grub_off_t buf_off,
                         grub_size_t bufsize)
@@ -117,6 +295,7 @@ grub_multiboot_make_mbi (void *orig, grub_uint32_t dest, grub_off_t buf_off,
   unsigned i;
   struct module *cur;
   grub_size_t mmap_size;
+  grub_err_t err;
 
   if (bufsize < grub_multiboot_get_mbi_size ())
     return grub_error (GRUB_ERR_OUT_OF_MEMORY, "mbi buffer is too small");
@@ -182,6 +361,19 @@ grub_multiboot_make_mbi (void *orig, grub_uint32_t dest, grub_off_t buf_off,
       mbi->flags |= MULTIBOOT_INFO_BOOTDEV;
     }
 
+  err = retrieve_video_parameters (mbi, ptrorig, ptrdest);
+  if (err)
+    {
+      grub_print_error ();
+      grub_errno = GRUB_ERR_NONE;
+    }
+#if HAS_VBE
+  ptrorig += sizeof (struct grub_vbe_info_block);
+  ptrdest += sizeof (struct grub_vbe_info_block);
+  ptrorig += sizeof (struct grub_vbe_mode_info_block);
+  ptrdest += sizeof (struct grub_vbe_mode_info_block);
+#endif
+
   return GRUB_ERR_NONE;
 }
 
index 30863c1edc0f4c580a55a25be5b7509d11eb054a..13ef0ddae8c9d4a52521ab579eada000d690a7fb 100644 (file)
@@ -353,6 +353,7 @@ grub_video_gop_get_info_and_fini (struct grub_video_mode_info *mode_info,
 static struct grub_video_adapter grub_video_gop_adapter =
   {
     .name = "EFI GOP driver",
+    .id = GRUB_VIDEO_DRIVER_EFI_GOP,
 
     .init = grub_video_gop_init,
     .fini = grub_video_gop_fini,
index 12ca35cdebb89148aa221acabe09652addbe1548..7ef7594ccf3910a335e19363dfdfa5da6063d988 100644 (file)
@@ -300,6 +300,7 @@ grub_video_uga_get_info_and_fini (struct grub_video_mode_info *mode_info,
 static struct grub_video_adapter grub_video_uga_adapter =
   {
     .name = "EFI UGA driver",
+    .id = GRUB_VIDEO_DRIVER_EFI_UGA,
 
     .init = grub_video_uga_init,
     .fini = grub_video_uga_fini,
index 17d9b328206e206efc4404a0ebcbe816c396cef9..918bab0b0cede06e3f7d14b5e9fa31e4e11bd737 100644 (file)
@@ -557,6 +557,7 @@ grub_video_vbe_get_info_and_fini (struct grub_video_mode_info *mode_info,
 static struct grub_video_adapter grub_video_vbe_adapter =
   {
     .name = "VESA BIOS Extension Video Driver",
+    .id = GRUB_VIDEO_DRIVER_VBE,
 
     .init = grub_video_vbe_init,
     .fini = grub_video_vbe_fini,
index 682bebcbfb6d255617d7d0b5c51b284eed26b6ad..e678c29829f9471fb2a3a8733af3b3c798391e3e 100644 (file)
@@ -93,6 +93,14 @@ grub_video_get_info (struct grub_video_mode_info *mode_info)
   return grub_video_adapter_active->get_info (mode_info);
 }
 
+grub_video_driver_id_t
+grub_video_get_driver_id (void)
+{
+  if (! grub_video_adapter_active)
+    return GRUB_VIDEO_DRIVER_NONE;
+  return grub_video_adapter_active->id;
+}
+
 /* Get information about active video mode.  */
 grub_err_t
 grub_video_get_info_and_fini (struct grub_video_mode_info *mode_info,