From 6323696adf0654123a2494b69c778375cc6c2a03 Mon Sep 17 00:00:00 2001 From: chaac Date: Mon, 15 Aug 2005 21:25:41 +0000 Subject: [PATCH] Added first version of the VBE terminal code. --- ChangeLog | 30 ++ DISTLIST | 4 + commands/i386/pc/vbe_list_modes.c | 143 +++++++ commands/i386/pc/vbe_test.c | 195 +++++++++ conf/i386-pc.mk | 203 +++++++++- conf/i386-pc.rmk | 19 +- include/grub/i386/pc/vbe.h | 15 + term/i386/pc/vesafb.c | 634 ++++++++++++++++++++++++++++++ video/i386/pc/vbe.c | 392 ++++++++++++++++++ 9 files changed, 1633 insertions(+), 2 deletions(-) create mode 100644 commands/i386/pc/vbe_list_modes.c create mode 100644 commands/i386/pc/vbe_test.c create mode 100644 term/i386/pc/vesafb.c create mode 100644 video/i386/pc/vbe.c diff --git a/ChangeLog b/ChangeLog index 32d0db32c..1f66c50db 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,33 @@ +2005-08-16 Vesa Jaaskelainen + + * DISTLIST: Added term/i386/pc/vesafb.c + DISTLIST: Added video/i386/pc/vbe.c + DISTLIST: Added commands/i386/pc/vbe_list_modes.c. + DISTLIST: Added commands/i386/pc/vbe_test.c. + * commands/i386/pc/vbe_list_modes.c: New file. + * commands/i386/pc/vbe_test.c: Likewise. + * term/i386/pc/vesafb.c: Likewise. + * video/i386/pc/vbe.c: Likewise. + * include/grub/i386/pc/vbe.h (GRUB_VBE_DEFAULT_VIDEO_MODE): Added define. + (grub_vbe_probe) Added prototype. + (grub_vbe_set_video_mode) Likewise. + (grub_vbe_get_video_mode) Likewise. + (grub_vbe_get_video_mode_info) Likewise. + (grub_vbe_set_pixel_rgb) Likewise. + (grub_vbe_set_pixel_index) Likewise. + * conf/i386-pc.rmk (pkgdata_MODULES): Added vbe.mod. + (pkgdata_MODULES): Added vesafb.mod. + (pkgdata_MODULES): Added vbe_list_modes.mod. + (pkgdata_MODULES): Added vbe_test.mod. + (vbe_mod_SOURCES): Added. + (vbe_mod_CFLAGS): Likewise. + (vesafb_mod_SOURCES): Likewise. + (vesafb_mod_CFLAGS): Likewise. + (vbe_list_modes_mod_SOURCES): Likewise. + (vbe_list_modes_mod_CFLAGS): Likewise. + (vbe_test_mod_SOURCES): Likewise. + (vbe_test_mod_CFLAGS): Likewise. + 2005-08-14 Yoshinori K. Okuji * normal/command.c (grub_command_execute): If INTERACTIVE is diff --git a/DISTLIST b/DISTLIST index 724d9f398..f92df267d 100644 --- a/DISTLIST +++ b/DISTLIST @@ -32,6 +32,8 @@ boot/powerpc/ieee1275/cmain.c boot/powerpc/ieee1275/crt0.S commands/i386/pc/halt.c commands/i386/pc/reboot.c +commands/i386/pc/vbe_list_modes.c +commands/i386/pc/vbe_test.c commands/ieee1275/halt.c commands/ieee1275/reboot.c commands/ieee1275/suspend.c @@ -163,6 +165,7 @@ partmap/apple.c partmap/pc.c partmap/sun.c term/i386/pc/console.c +term/i386/pc/vesafb.c term/i386/pc/vga.c term/ieee1275/ofconsole.c util/i386/pc/biosdisk.c @@ -180,3 +183,4 @@ util/grub-emu.c util/misc.c util/resolve.c util/unifont2pff.rb +video/i386/pc/vbe.c diff --git a/commands/i386/pc/vbe_list_modes.c b/commands/i386/pc/vbe_list_modes.c new file mode 100644 index 000000000..d4e07a69c --- /dev/null +++ b/commands/i386/pc/vbe_list_modes.c @@ -0,0 +1,143 @@ +/* vbe_list_modes.c - command to list compatible VBE video modes. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005 Free Software Foundation, Inc. + * + * GRUB is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +static void * +real2pm(grub_vbe_farptr_t ptr) +{ + return (void *)((((unsigned long)ptr & 0xFFFF0000) >> 12UL) + + ((unsigned long)ptr & 0x0000FFFF)); +} + +static grub_err_t +grub_cmd_vbe_list_modes(struct grub_arg_list *state __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + struct grub_vbe_info_block controller_info; + struct grub_vbe_mode_info_block mode_info_tmp; + grub_uint32_t use_mode = GRUB_VBE_DEFAULT_VIDEO_MODE; + grub_uint16_t *sptr; + grub_uint32_t rc; + char *modevar; + + grub_printf ("List of compatible video modes:\n"); + + rc = grub_vbe_probe (&controller_info); + + if (rc != GRUB_ERR_NONE) + { + grub_printf ("VESA BIOS Extension not detected!\n"); + return rc; + } + + sptr = real2pm (controller_info.video_mode_ptr); + + /* Walk thru all video modes listed. */ + for (;*sptr != 0xFFFF; sptr++) + { + int valid_mode = 1; + + rc = grub_vbe_get_video_mode_info (*sptr, &mode_info_tmp); + if (rc != GRUB_ERR_NONE) continue; + + if ((mode_info_tmp.mode_attributes & 0x080) == 0) + { + /* We support only linear frame buffer modes. */ + continue; + } + + if ((mode_info_tmp.mode_attributes & 0x010) == 0) + { + /* We allow only graphical modes. */ + continue; + } + + switch(mode_info_tmp.memory_model) + { + case 0x04: + case 0x06: + break; + + default: + valid_mode = 0; + break; + } + + if (valid_mode == 0) continue; + + grub_printf ("0x%03x: %d x %d x %d bpp\n", + *sptr, + mode_info_tmp.x_resolution, + mode_info_tmp.y_resolution, + mode_info_tmp.bits_per_pixel); + } + + /* Check existence of vbe_mode environment variable. */ + modevar = grub_env_get ("vbe_mode"); + + /* Check existence of vbe_mode environment variable. */ + modevar = grub_env_get ("vbe_mode"); + + if (modevar != 0) + { + unsigned long value = 0; + + if ((grub_strncmp (modevar, "0x", 2) == 0) || + (grub_strncmp (modevar, "0X", 2) == 0)) + { + /* Convert HEX mode number. */ + value = grub_strtoul (modevar + 2, 0, 16); + } + else + { + /* Convert DEC mode number. */ + value = grub_strtoul (modevar, 0, 10); + } + + if (value != 0) + { + use_mode = value; + } + } + + grub_printf ("Configured VBE mode (vbe_mode) = 0x%03x\n", use_mode); + + return 0; +} + +GRUB_MOD_INIT +{ + (void)mod; /* To stop warning. */ + grub_register_command ("vbe_list_modes", grub_cmd_vbe_list_modes, GRUB_COMMAND_FLAG_BOTH, + "vbe_list_modes", "List compatible VESA BIOS extension video modes.", 0); +} + +GRUB_MOD_FINI +{ + grub_unregister_command ("vbe_list_modes"); +} diff --git a/commands/i386/pc/vbe_test.c b/commands/i386/pc/vbe_test.c new file mode 100644 index 000000000..b0cbecb8c --- /dev/null +++ b/commands/i386/pc/vbe_test.c @@ -0,0 +1,195 @@ +/* vbe_test.c - command to test VESA BIOS Extension 2.0+ support. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005 Free Software Foundation, Inc. + * + * GRUB is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static void * +real2pm(grub_vbe_farptr_t ptr) +{ + return (void *)((((unsigned long)ptr & 0xFFFF0000) >> 12UL) + + ((unsigned long)ptr & 0x0000FFFF)); +} + +static grub_err_t +grub_cmd_vbe_test(struct grub_arg_list *state __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + grub_uint32_t rc; + grub_uint16_t *sptr; + char *modevar; + struct grub_vbe_mode_info_block mode_info; + struct grub_vbe_info_block controller_info; + grub_uint32_t use_mode = GRUB_VBE_DEFAULT_VIDEO_MODE; + grub_uint32_t old_mode; + grub_uint8_t *framebuffer = 0; + grub_uint32_t bytes_per_scan_line = 0; + unsigned char *ptr; + int i; + + grub_printf ("Probing for VESA BIOS Extension ... "); + + /* Check if VESA BIOS exists. */ + rc = grub_vbe_probe(&controller_info); + if (rc != GRUB_ERR_NONE) + { + grub_printf ("not found!\n"); + return rc; + } + grub_printf ("found!\n"); + + /* Dump out controller information. */ + grub_printf ("VBE signature = %c%c%c%c\n", + controller_info.signature[0], + controller_info.signature[1], + controller_info.signature[2], + controller_info.signature[3]); + + grub_printf ("VBE version = %d.%d\n", + controller_info.version >> 8, + controller_info.version & 0xFF); + grub_printf ("OEM string ptr = %08x\n", + controller_info.oem_string_ptr); + grub_printf ("Total memory = %d\n", + controller_info.total_memory); + + sptr = real2pm(controller_info.video_mode_ptr); + + rc = grub_vbe_get_video_mode(&old_mode); + grub_printf ("Get video mode rc = %04x\n", rc); + + if (rc == GRUB_ERR_NONE) + { + grub_printf ("Old video mode = %04x\n", old_mode); + } + + /* Check existence of vbe_mode environment variable. */ + modevar = grub_env_get ("vbe_mode"); + + if (modevar != 0) + { + unsigned long value = 0; + + if ((grub_strncmp (modevar, "0x", 2) == 0) || + (grub_strncmp (modevar, "0X", 2) == 0)) + { + /* Convert HEX mode number. */ + value = grub_strtoul (modevar + 2, 0, 16); + } + else + { + /* Convert DEC mode number. */ + value = grub_strtoul (modevar, 0, 10); + } + + if (value != 0) + { + use_mode = value; + } + } + + rc = grub_vbe_get_video_mode_info (use_mode, &mode_info); + + /* Dump out details about the mode being tested. */ + grub_printf ("mode: 0x%03x\n", + use_mode); + grub_printf ("width : %d\n", + mode_info.x_resolution); + grub_printf ("height: %d\n", + mode_info.y_resolution); + grub_printf ("memory model: %02x\n", + mode_info.memory_model); + grub_printf ("bytes/scanline: %d\n", + mode_info.bytes_per_scan_line); + grub_printf ("bytes/scanline (lin): %d\n", + mode_info.lin_bytes_per_scan_line); + grub_printf ("base address: %08x\n", + mode_info.phys_base_addr); + grub_printf ("red mask/pos: %d/%d\n", + mode_info.red_mask_size, + mode_info.red_field_position); + grub_printf ("green mask/pos: %d/%d\n", + mode_info.green_mask_size, + mode_info.green_field_position); + grub_printf ("blue mask/pos: %d/%d\n", + mode_info.blue_mask_size, + mode_info.blue_field_position); + + grub_printf ("Press any key to continue.\n"); + + grub_getkey (); + + /* Setup GFX mode. */ + rc = grub_vbe_set_video_mode (use_mode, &mode_info); + + /* Determine framebuffer address and how many bytes are in scan line. */ + framebuffer = (grub_uint8_t *)mode_info.phys_base_addr; + ptr = framebuffer; + + if (controller_info.version >= 0x300) + { + bytes_per_scan_line = mode_info.lin_bytes_per_scan_line; + } + else + { + bytes_per_scan_line = mode_info.bytes_per_scan_line; + } + + /* Draw some random data to screen. */ + for (i = 0; i < 256 * 256; i++) + { + ptr[i] = i & 0x0F; + } + + /* Draw white line to screen. */ + for (i = 0; i < 100; i++) + { + ptr[mode_info.bytes_per_scan_line * 50 + i] = 0x0F; + } + + /* Draw another white line to screen. */ + grub_memset (ptr + bytes_per_scan_line * 51, 0x0f, bytes_per_scan_line); + + grub_getkey (); + + /* Restore old video mode. */ + grub_vbe_set_video_mode (old_mode, 0); + + return 0; +} + +GRUB_MOD_INIT +{ + (void)mod; /* To stop warning. */ + grub_register_command ("vbe_test", grub_cmd_vbe_test, GRUB_COMMAND_FLAG_BOTH, + "vbe_test", "Test VESA BIOS Extension 2.0+ support", 0); +} + +GRUB_MOD_FINI +{ + grub_unregister_command ("vbe_test"); +} diff --git a/conf/i386-pc.mk b/conf/i386-pc.mk index fd41dbb3c..0d65502e4 100644 --- a/conf/i386-pc.mk +++ b/conf/i386-pc.mk @@ -1141,7 +1141,8 @@ pkgdata_MODULES = _chain.mod _linux.mod linux.mod fat.mod ufs.mod \ font.mod _multiboot.mod ls.mod boot.mod cmp.mod cat.mod \ terminal.mod fshelp.mod chain.mod multiboot.mod amiga.mod \ apple.mod pc.mod sun.mod loopback.mod reboot.mod halt.mod \ - help.mod default.mod timeout.mod configfile.mod + help.mod default.mod timeout.mod configfile.mod vbe.mod \ + vesafb.mod vbe_test.mod vbe_list_modes.mod # For _chain.mod. _chain_mod_SOURCES = loader/i386/pc/chainloader.c @@ -2951,6 +2952,206 @@ fs-configfile.lst: commands/configfile.c genfslist.sh configfile_mod_CFLAGS = $(COMMON_CFLAGS) + +# For vbe.mod. +vbe_mod_SOURCES = video/i386/pc/vbe.c +CLEANFILES += vbe.mod mod-vbe.o mod-vbe.c pre-vbe.o vbe_mod-video_i386_pc_vbe.o def-vbe.lst und-vbe.lst +MOSTLYCLEANFILES += vbe_mod-video_i386_pc_vbe.d +DEFSYMFILES += def-vbe.lst +UNDSYMFILES += und-vbe.lst + +vbe.mod: pre-vbe.o mod-vbe.o + -rm -f $@ + $(LD) -r -d -o $@ $^ + $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -R .note -R .comment $@ + +pre-vbe.o: vbe_mod-video_i386_pc_vbe.o + -rm -f $@ + $(LD) -r -d -o $@ $^ + +mod-vbe.o: mod-vbe.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(vbe_mod_CFLAGS) -c -o $@ $< + +mod-vbe.c: moddep.lst genmodsrc.sh + sh $(srcdir)/genmodsrc.sh 'vbe' $< > $@ || (rm -f $@; exit 1) + +def-vbe.lst: pre-vbe.o + $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 vbe/' > $@ + +und-vbe.lst: pre-vbe.o + echo 'vbe' > $@ + $(NM) -u -P -p $< | cut -f1 -d' ' >> $@ + +vbe_mod-video_i386_pc_vbe.o: video/i386/pc/vbe.c + $(CC) -Ivideo/i386/pc -I$(srcdir)/video/i386/pc $(CPPFLAGS) $(CFLAGS) $(vbe_mod_CFLAGS) -c -o $@ $< + +vbe_mod-video_i386_pc_vbe.d: video/i386/pc/vbe.c + set -e; $(CC) -Ivideo/i386/pc -I$(srcdir)/video/i386/pc $(CPPFLAGS) $(CFLAGS) $(vbe_mod_CFLAGS) -M $< | sed 's,vbe\.o[ :]*,vbe_mod-video_i386_pc_vbe.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@ + +-include vbe_mod-video_i386_pc_vbe.d + +CLEANFILES += cmd-vbe.lst fs-vbe.lst +COMMANDFILES += cmd-vbe.lst +FSFILES += fs-vbe.lst + +cmd-vbe.lst: video/i386/pc/vbe.c gencmdlist.sh + set -e; $(CC) -Ivideo/i386/pc -I$(srcdir)/video/i386/pc $(CPPFLAGS) $(CFLAGS) $(vbe_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh vbe > $@ || (rm -f $@; exit 1) + +fs-vbe.lst: video/i386/pc/vbe.c genfslist.sh + set -e; $(CC) -Ivideo/i386/pc -I$(srcdir)/video/i386/pc $(CPPFLAGS) $(CFLAGS) $(vbe_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh vbe > $@ || (rm -f $@; exit 1) + + +vbe_mod_CFLAGS = $(COMMON_CFLAGS) + +# For vesafb.mod. +vesafb_mod_SOURCES = term/i386/pc/vesafb.c +CLEANFILES += vesafb.mod mod-vesafb.o mod-vesafb.c pre-vesafb.o vesafb_mod-term_i386_pc_vesafb.o def-vesafb.lst und-vesafb.lst +MOSTLYCLEANFILES += vesafb_mod-term_i386_pc_vesafb.d +DEFSYMFILES += def-vesafb.lst +UNDSYMFILES += und-vesafb.lst + +vesafb.mod: pre-vesafb.o mod-vesafb.o + -rm -f $@ + $(LD) -r -d -o $@ $^ + $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -R .note -R .comment $@ + +pre-vesafb.o: vesafb_mod-term_i386_pc_vesafb.o + -rm -f $@ + $(LD) -r -d -o $@ $^ + +mod-vesafb.o: mod-vesafb.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(vesafb_mod_CFLAGS) -c -o $@ $< + +mod-vesafb.c: moddep.lst genmodsrc.sh + sh $(srcdir)/genmodsrc.sh 'vesafb' $< > $@ || (rm -f $@; exit 1) + +def-vesafb.lst: pre-vesafb.o + $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 vesafb/' > $@ + +und-vesafb.lst: pre-vesafb.o + echo 'vesafb' > $@ + $(NM) -u -P -p $< | cut -f1 -d' ' >> $@ + +vesafb_mod-term_i386_pc_vesafb.o: term/i386/pc/vesafb.c + $(CC) -Iterm/i386/pc -I$(srcdir)/term/i386/pc $(CPPFLAGS) $(CFLAGS) $(vesafb_mod_CFLAGS) -c -o $@ $< + +vesafb_mod-term_i386_pc_vesafb.d: term/i386/pc/vesafb.c + set -e; $(CC) -Iterm/i386/pc -I$(srcdir)/term/i386/pc $(CPPFLAGS) $(CFLAGS) $(vesafb_mod_CFLAGS) -M $< | sed 's,vesafb\.o[ :]*,vesafb_mod-term_i386_pc_vesafb.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@ + +-include vesafb_mod-term_i386_pc_vesafb.d + +CLEANFILES += cmd-vesafb.lst fs-vesafb.lst +COMMANDFILES += cmd-vesafb.lst +FSFILES += fs-vesafb.lst + +cmd-vesafb.lst: term/i386/pc/vesafb.c gencmdlist.sh + set -e; $(CC) -Iterm/i386/pc -I$(srcdir)/term/i386/pc $(CPPFLAGS) $(CFLAGS) $(vesafb_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh vesafb > $@ || (rm -f $@; exit 1) + +fs-vesafb.lst: term/i386/pc/vesafb.c genfslist.sh + set -e; $(CC) -Iterm/i386/pc -I$(srcdir)/term/i386/pc $(CPPFLAGS) $(CFLAGS) $(vesafb_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh vesafb > $@ || (rm -f $@; exit 1) + + +vesafb_mod_CFLAGS = $(COMMON_CFLAGS) + +# For vbe_list_modes.mod. +vbe_list_modes_mod_SOURCES = commands/i386/pc/vbe_list_modes.c +CLEANFILES += vbe_list_modes.mod mod-vbe_list_modes.o mod-vbe_list_modes.c pre-vbe_list_modes.o vbe_list_modes_mod-commands_i386_pc_vbe_list_modes.o def-vbe_list_modes.lst und-vbe_list_modes.lst +MOSTLYCLEANFILES += vbe_list_modes_mod-commands_i386_pc_vbe_list_modes.d +DEFSYMFILES += def-vbe_list_modes.lst +UNDSYMFILES += und-vbe_list_modes.lst + +vbe_list_modes.mod: pre-vbe_list_modes.o mod-vbe_list_modes.o + -rm -f $@ + $(LD) -r -d -o $@ $^ + $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -R .note -R .comment $@ + +pre-vbe_list_modes.o: vbe_list_modes_mod-commands_i386_pc_vbe_list_modes.o + -rm -f $@ + $(LD) -r -d -o $@ $^ + +mod-vbe_list_modes.o: mod-vbe_list_modes.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(vbe_list_modes_mod_CFLAGS) -c -o $@ $< + +mod-vbe_list_modes.c: moddep.lst genmodsrc.sh + sh $(srcdir)/genmodsrc.sh 'vbe_list_modes' $< > $@ || (rm -f $@; exit 1) + +def-vbe_list_modes.lst: pre-vbe_list_modes.o + $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 vbe_list_modes/' > $@ + +und-vbe_list_modes.lst: pre-vbe_list_modes.o + echo 'vbe_list_modes' > $@ + $(NM) -u -P -p $< | cut -f1 -d' ' >> $@ + +vbe_list_modes_mod-commands_i386_pc_vbe_list_modes.o: commands/i386/pc/vbe_list_modes.c + $(CC) -Icommands/i386/pc -I$(srcdir)/commands/i386/pc $(CPPFLAGS) $(CFLAGS) $(vbe_list_modes_mod_CFLAGS) -c -o $@ $< + +vbe_list_modes_mod-commands_i386_pc_vbe_list_modes.d: commands/i386/pc/vbe_list_modes.c + set -e; $(CC) -Icommands/i386/pc -I$(srcdir)/commands/i386/pc $(CPPFLAGS) $(CFLAGS) $(vbe_list_modes_mod_CFLAGS) -M $< | sed 's,vbe_list_modes\.o[ :]*,vbe_list_modes_mod-commands_i386_pc_vbe_list_modes.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@ + +-include vbe_list_modes_mod-commands_i386_pc_vbe_list_modes.d + +CLEANFILES += cmd-vbe_list_modes.lst fs-vbe_list_modes.lst +COMMANDFILES += cmd-vbe_list_modes.lst +FSFILES += fs-vbe_list_modes.lst + +cmd-vbe_list_modes.lst: commands/i386/pc/vbe_list_modes.c gencmdlist.sh + set -e; $(CC) -Icommands/i386/pc -I$(srcdir)/commands/i386/pc $(CPPFLAGS) $(CFLAGS) $(vbe_list_modes_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh vbe_list_modes > $@ || (rm -f $@; exit 1) + +fs-vbe_list_modes.lst: commands/i386/pc/vbe_list_modes.c genfslist.sh + set -e; $(CC) -Icommands/i386/pc -I$(srcdir)/commands/i386/pc $(CPPFLAGS) $(CFLAGS) $(vbe_list_modes_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh vbe_list_modes > $@ || (rm -f $@; exit 1) + + +vbe_list_modes_mod_CFLAGS = $(COMMON_CFLAGS) + +# For vbe_test.mod. +vbe_test_mod_SOURCES = commands/i386/pc/vbe_test.c +CLEANFILES += vbe_test.mod mod-vbe_test.o mod-vbe_test.c pre-vbe_test.o vbe_test_mod-commands_i386_pc_vbe_test.o def-vbe_test.lst und-vbe_test.lst +MOSTLYCLEANFILES += vbe_test_mod-commands_i386_pc_vbe_test.d +DEFSYMFILES += def-vbe_test.lst +UNDSYMFILES += und-vbe_test.lst + +vbe_test.mod: pre-vbe_test.o mod-vbe_test.o + -rm -f $@ + $(LD) -r -d -o $@ $^ + $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -R .note -R .comment $@ + +pre-vbe_test.o: vbe_test_mod-commands_i386_pc_vbe_test.o + -rm -f $@ + $(LD) -r -d -o $@ $^ + +mod-vbe_test.o: mod-vbe_test.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(vbe_test_mod_CFLAGS) -c -o $@ $< + +mod-vbe_test.c: moddep.lst genmodsrc.sh + sh $(srcdir)/genmodsrc.sh 'vbe_test' $< > $@ || (rm -f $@; exit 1) + +def-vbe_test.lst: pre-vbe_test.o + $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 vbe_test/' > $@ + +und-vbe_test.lst: pre-vbe_test.o + echo 'vbe_test' > $@ + $(NM) -u -P -p $< | cut -f1 -d' ' >> $@ + +vbe_test_mod-commands_i386_pc_vbe_test.o: commands/i386/pc/vbe_test.c + $(CC) -Icommands/i386/pc -I$(srcdir)/commands/i386/pc $(CPPFLAGS) $(CFLAGS) $(vbe_test_mod_CFLAGS) -c -o $@ $< + +vbe_test_mod-commands_i386_pc_vbe_test.d: commands/i386/pc/vbe_test.c + set -e; $(CC) -Icommands/i386/pc -I$(srcdir)/commands/i386/pc $(CPPFLAGS) $(CFLAGS) $(vbe_test_mod_CFLAGS) -M $< | sed 's,vbe_test\.o[ :]*,vbe_test_mod-commands_i386_pc_vbe_test.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@ + +-include vbe_test_mod-commands_i386_pc_vbe_test.d + +CLEANFILES += cmd-vbe_test.lst fs-vbe_test.lst +COMMANDFILES += cmd-vbe_test.lst +FSFILES += fs-vbe_test.lst + +cmd-vbe_test.lst: commands/i386/pc/vbe_test.c gencmdlist.sh + set -e; $(CC) -Icommands/i386/pc -I$(srcdir)/commands/i386/pc $(CPPFLAGS) $(CFLAGS) $(vbe_test_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh vbe_test > $@ || (rm -f $@; exit 1) + +fs-vbe_test.lst: commands/i386/pc/vbe_test.c genfslist.sh + set -e; $(CC) -Icommands/i386/pc -I$(srcdir)/commands/i386/pc $(CPPFLAGS) $(CFLAGS) $(vbe_test_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh vbe_test > $@ || (rm -f $@; exit 1) + + +vbe_test_mod_CFLAGS = $(COMMON_CFLAGS) CLEANFILES += moddep.lst command.lst fs.lst pkgdata_DATA += moddep.lst command.lst fs.lst moddep.lst: $(DEFSYMFILES) $(UNDSYMFILES) genmoddep diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index aa5d4f8f6..3e93b2c86 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -111,7 +111,8 @@ pkgdata_MODULES = _chain.mod _linux.mod linux.mod fat.mod ufs.mod \ font.mod _multiboot.mod ls.mod boot.mod cmp.mod cat.mod \ terminal.mod fshelp.mod chain.mod multiboot.mod amiga.mod \ apple.mod pc.mod sun.mod loopback.mod reboot.mod halt.mod \ - help.mod default.mod timeout.mod configfile.mod + help.mod default.mod timeout.mod configfile.mod vbe.mod \ + vesafb.mod vbe_test.mod vbe_list_modes.mod # For _chain.mod. _chain_mod_SOURCES = loader/i386/pc/chainloader.c @@ -251,3 +252,19 @@ timeout_mod_CFLAGS = $(COMMON_CFLAGS) # For configfile.mod configfile_mod_SOURCES = commands/configfile.c configfile_mod_CFLAGS = $(COMMON_CFLAGS) + +# For vbe.mod. +vbe_mod_SOURCES = video/i386/pc/vbe.c +vbe_mod_CFLAGS = $(COMMON_CFLAGS) + +# For vesafb.mod. +vesafb_mod_SOURCES = term/i386/pc/vesafb.c +vesafb_mod_CFLAGS = $(COMMON_CFLAGS) + +# For vbe_list_modes.mod. +vbe_list_modes_mod_SOURCES = commands/i386/pc/vbe_list_modes.c +vbe_list_modes_mod_CFLAGS = $(COMMON_CFLAGS) + +# For vbe_test.mod. +vbe_test_mod_SOURCES = commands/i386/pc/vbe_test.c +vbe_test_mod_CFLAGS = $(COMMON_CFLAGS) diff --git a/include/grub/i386/pc/vbe.h b/include/grub/i386/pc/vbe.h index 8f7781f32..bade315bd 100644 --- a/include/grub/i386/pc/vbe.h +++ b/include/grub/i386/pc/vbe.h @@ -22,6 +22,10 @@ #include #include +#include + +/* Default video mode to be used. */ +#define GRUB_VBE_DEFAULT_VIDEO_MODE 0x101 /* Note: @@ -140,6 +144,8 @@ struct grub_vbe_palette_data grub_uint8_t aligment; } __attribute__ ((packed)); +/* Prototypes for kernel real mode thunks. */ + /* Call VESA BIOS 0x4f00 to get VBE Controller Information, return status. */ grub_vbe_status_t EXPORT_FUNC(grub_vbe_get_controller_info) (struct grub_vbe_info_block *controller_info); @@ -181,4 +187,13 @@ grub_vbe_status_t EXPORT_FUNC(grub_vbe_set_palette_data) (grub_uint32_t color_co grub_uint32_t start_index, struct grub_vbe_palette_data *palette_data); +/* Prototypes for helper functions. */ + +grub_err_t grub_vbe_probe (struct grub_vbe_info_block *info_block); +grub_err_t grub_vbe_set_video_mode (grub_uint32_t mode, struct grub_vbe_mode_info_block *mode_info); +grub_err_t grub_vbe_get_video_mode (grub_uint32_t *mode); +grub_err_t grub_vbe_get_video_mode_info (grub_uint32_t mode, struct grub_vbe_mode_info_block *mode_info); +void grub_vbe_set_pixel_rgb (grub_uint32_t x, grub_uint32_t y, grub_uint8_t red, grub_uint8_t green, grub_uint8_t blue); +void grub_vbe_set_pixel_index (grub_uint32_t x, grub_uint32_t y, grub_uint8_t color); + #endif /* ! GRUB_VBE_MACHINE_HEADER */ diff --git a/term/i386/pc/vesafb.c b/term/i386/pc/vesafb.c new file mode 100644 index 000000000..f1b72a2a9 --- /dev/null +++ b/term/i386/pc/vesafb.c @@ -0,0 +1,634 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_CHAR_WIDTH 8 +#define DEFAULT_CHAR_HEIGHT 16 + +#define DEFAULT_FG_COLOR 0xa +#define DEFAULT_BG_COLOR 0x0 + +struct grub_colored_char +{ + /* An Unicode codepoint. */ + grub_uint32_t code; + + /* Color indexes. */ + unsigned char fg_color; + unsigned char bg_color; + + /* The width of this character minus one. */ + unsigned char width; + + /* The column index of this character. */ + unsigned char index; +}; + +struct grub_virtual_screen +{ + /* Dimensions of the virual screen. */ + grub_uint32_t width; + grub_uint32_t height; + + /* Offset in the display. */ + grub_uint32_t offset_x; + grub_uint32_t offset_y; + + /* TTY Character sizes. */ + grub_uint32_t char_width; + grub_uint32_t char_height; + + /* Virtual screen TTY size. */ + grub_uint32_t columns; + grub_uint32_t rows; + + /* Current cursor details. */ + grub_uint32_t cursor_x; + grub_uint32_t cursor_y; + grub_uint8_t cursor_state; + grub_uint8_t fg_color; + grub_uint8_t bg_color; + + /* Text buffer for virual screen. Contains (columns * rows) number + of entries. */ + struct grub_colored_char *text_buffer; +}; + +/* Make seure text buffer is not marked as allocated. */ +static struct grub_virtual_screen virtual_screen = + { + .text_buffer = 0 + }; + +static grub_dl_t my_mod; +static unsigned char *vga_font = 0; +static grub_uint32_t old_mode = 0; + +static struct grub_vbe_mode_info_block mode_info; +static grub_uint8_t *framebuffer = 0; +static grub_uint32_t bytes_per_scan_line = 0; + +static void +grub_virtual_screen_free (void) +{ + /* If virtual screen has been allocated, free it. */ + if (virtual_screen.text_buffer != 0) + { + grub_free (virtual_screen.text_buffer); + } + + /* Reset virtual screen data. */ + grub_memset (&virtual_screen, 0, sizeof(virtual_screen)); +} + +static grub_err_t +grub_virtual_screen_setup (grub_uint32_t width, + grub_uint32_t height) +{ + /* Free old virtual screen. */ + grub_virtual_screen_free (); + + /* Initialize with default data. */ + virtual_screen.width = width; + virtual_screen.height = height; + virtual_screen.offset_x = 0; + virtual_screen.offset_y = 0; + virtual_screen.char_width = DEFAULT_CHAR_WIDTH; + virtual_screen.char_height = DEFAULT_CHAR_HEIGHT; + virtual_screen.cursor_x = 0; + virtual_screen.cursor_y = 0; + virtual_screen.cursor_state = 1; + virtual_screen.fg_color = DEFAULT_FG_COLOR; + virtual_screen.bg_color = DEFAULT_BG_COLOR; + + /* Calculate size of text buffer. */ + virtual_screen.columns = virtual_screen.width / virtual_screen.char_width; + virtual_screen.rows = virtual_screen.height / virtual_screen.char_height; + + /* Allocate memory for text buffer. */ + virtual_screen.text_buffer = + (struct grub_colored_char *)grub_malloc (virtual_screen.columns * + virtual_screen.rows * + sizeof(struct grub_colored_char)); + if (virtual_screen.text_buffer == 0) + { + return GRUB_ERR_OUT_OF_MEMORY; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_vesafb_init (void) +{ + grub_uint32_t rc; + grub_uint32_t use_mode = GRUB_VBE_DEFAULT_VIDEO_MODE; + struct grub_vbe_info_block controller_info; + char *modevar; + + /* Use fonts from VGA bios. */ + vga_font = grub_vga_get_font (); + + /* Check if we have VESA BIOS installed. */ + rc = grub_vbe_probe (&controller_info); + if (rc != GRUB_ERR_NONE) + { + return rc; + } + + /* Check existence of vbe_mode environment variable. */ + modevar = grub_env_get ("vbe_mode"); + + if (modevar != 0) + { + unsigned long value = 0; + + if ((grub_strncmp (modevar, "0x", 2) == 0) || + (grub_strncmp (modevar, "0X", 2) == 0)) + { + /* Convert HEX mode number. */ + value = grub_strtoul (modevar + 2, 0, 16); + } + else + { + /* Convert DEC mode number. */ + value = grub_strtoul (modevar, 0, 10); + } + + if (value != 0) + { + use_mode = value; + } + } + + /* Store initial video mode. */ + rc = grub_vbe_get_video_mode (&old_mode); + + /* Setup desired graphics mode. */ + rc = grub_vbe_set_video_mode (use_mode, &mode_info); + if (rc != GRUB_ERR_NONE) + { + return rc; + } + + /* Determine framebuffer and bytes per scan line. */ + framebuffer = (grub_uint8_t *)mode_info.phys_base_addr; + + if (controller_info.version >= 0x300) + { + bytes_per_scan_line = mode_info.lin_bytes_per_scan_line; + } + else + { + bytes_per_scan_line = mode_info.bytes_per_scan_line; + } + + /* Create virtual screen. */ + rc = grub_virtual_screen_setup (mode_info.x_resolution, + mode_info.y_resolution); + if (rc != GRUB_ERR_NONE) + { + grub_vbe_set_video_mode (old_mode, 0); + return rc; + } + + /* Make sure frame buffer is black. */ + grub_memset (framebuffer, + 0, + bytes_per_scan_line * mode_info.y_resolution); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_vesafb_fini (void) +{ + grub_virtual_screen_free (); + + grub_vbe_set_video_mode (old_mode, 0); + + return GRUB_ERR_NONE; +} + +static int +grub_virtual_screen_get_glyph (grub_uint32_t code, + unsigned char bitmap[32], + unsigned *width) +{ + if (code > 0x7f) + { + /* Map some unicode characters to the VGA font, if possible. */ + switch (code) + { + case 0x2190: /* left arrow */ + code = 0x1b; + break; + case 0x2191: /* up arrow */ + code = 0x18; + break; + case 0x2192: /* right arrow */ + code = 0x1a; + break; + case 0x2193: /* down arrow */ + code = 0x19; + break; + case 0x2501: /* horizontal line */ + code = 0xc4; + break; + case 0x2503: /* vertical line */ + code = 0xb3; + break; + case 0x250F: /* upper-left corner */ + code = 0xda; + break; + case 0x2513: /* upper-right corner */ + code = 0xbf; + break; + case 0x2517: /* lower-left corner */ + code = 0xc0; + break; + case 0x251B: /* lower-right corner */ + code = 0xd9; + break; + + default: + return grub_font_get_glyph (code, bitmap, width); + } + } + + if (bitmap) + grub_memcpy (bitmap, + vga_font + code * virtual_screen.char_height, + virtual_screen.char_height); + *width = 1; + return 1; +} + +static void +grub_virtual_screen_invalidate_char (struct grub_colored_char *p) +{ + p->code = 0xFFFF; + + if (p->width) + { + struct grub_colored_char *q; + + for (q = p + 1; q <= p + p->width; q++) + { + q->code = 0xFFFF; + q->width = 0; + q->index = 0; + } + } + + p->width = 0; +} + +static void +write_char (void) +{ + struct grub_colored_char *p; + unsigned char bitmap[32]; + unsigned width; + + p = (virtual_screen.text_buffer + + virtual_screen.cursor_x + + (virtual_screen.cursor_y * virtual_screen.columns)); + + p -= p->index; + + if (! grub_virtual_screen_get_glyph (p->code, bitmap, &width)) + { + grub_virtual_screen_invalidate_char (p); + width = 0; + } + + unsigned y; + unsigned offset; + + for (y = 0, offset = 0; + y < virtual_screen.char_height; + y++, offset++) + { + unsigned i; + + for (i = 0; + (i < width * virtual_screen.char_width) && (offset < 32); + i++) + { + unsigned char color; + + if (bitmap[offset] & (1<<(8-i))) + { + color = p->fg_color; + } + else + { + color = p->bg_color; + } + + grub_vbe_set_pixel_index(i + (virtual_screen.cursor_x * virtual_screen.char_width), + y + (virtual_screen.cursor_y * virtual_screen.char_height), + color); + } + } +} + +static void +write_cursor (void) +{ + grub_uint32_t x; + grub_uint32_t y; + + for (y = ((virtual_screen.cursor_y + 1) * virtual_screen.char_height) - 3; + y < ((virtual_screen.cursor_y + 1) * virtual_screen.char_height) - 1; + y++) + { + for (x = virtual_screen.cursor_x * virtual_screen.char_width; + x < (virtual_screen.cursor_x + 1) * virtual_screen.char_width; + x++) + { + grub_vbe_set_pixel_index(x, y, 10); + } + } +} + +static void +scroll_up (void) +{ + grub_uint32_t i; + + /* Scroll text buffer with one line to up. */ + grub_memmove (virtual_screen.text_buffer, + virtual_screen.text_buffer + virtual_screen.columns, + sizeof (struct grub_colored_char) * + virtual_screen.columns * + (virtual_screen.rows - 1)); + + /* Clear last line in text buffer. */ + for (i = virtual_screen.columns * (virtual_screen.rows - 1); + i < virtual_screen.columns * virtual_screen.rows; + i++) + { + virtual_screen.text_buffer[i].code = ' '; + virtual_screen.text_buffer[i].fg_color = 0; + virtual_screen.text_buffer[i].bg_color = 0; + virtual_screen.text_buffer[i].width = 0; + virtual_screen.text_buffer[i].index = 0; + } + + /* Scroll frambuffer with one line to up. */ + grub_memmove (framebuffer, + framebuffer + bytes_per_scan_line * virtual_screen.char_height, + bytes_per_scan_line * + (mode_info.y_resolution - virtual_screen.char_height)); + + /* Clear last line in framebuffer. */ + grub_memset (framebuffer + + (bytes_per_scan_line * + (mode_info.y_resolution - virtual_screen.char_height)), + 0, + bytes_per_scan_line * virtual_screen.char_height); +} + +static void +grub_vesafb_putchar (grub_uint32_t c) +{ + if (c == '\a') + /* FIXME */ + return; + + if (c == '\b' || c == '\n' || c == '\r') + { + /* Erase current cursor, if any. */ + if (virtual_screen.cursor_state) + write_char (); + + switch (c) + { + case '\b': + if (virtual_screen.cursor_x > 0) + virtual_screen.cursor_x--; + break; + + case '\n': + if (virtual_screen.cursor_y >= virtual_screen.rows - 1) + scroll_up (); + else + virtual_screen.cursor_y++; + break; + + case '\r': + virtual_screen.cursor_x = 0; + break; + } + + if (virtual_screen.cursor_state) + write_cursor (); + } + else + { + unsigned width; + struct grub_colored_char *p; + + grub_virtual_screen_get_glyph (c, 0, &width); + + if (virtual_screen.cursor_x + width > virtual_screen.columns) + grub_putchar ('\n'); + + p = virtual_screen.text_buffer + + virtual_screen.cursor_x + + virtual_screen.cursor_y * virtual_screen.columns; + p->code = c; + p->fg_color = virtual_screen.fg_color; + p->bg_color = virtual_screen.bg_color; + p->width = width - 1; + p->index = 0; + + if (width > 1) + { + unsigned i; + + for (i = 1; i < width; i++) + { + p[i].code = ' '; + p[i].width = width - 1; + p[i].index = i; + } + } + + write_char (); + + virtual_screen.cursor_x += width; + if (virtual_screen.cursor_x >= virtual_screen.columns) + { + virtual_screen.cursor_x = 0; + + if (virtual_screen.cursor_y >= virtual_screen.rows - 1) + scroll_up (); + else + virtual_screen.cursor_y++; + } + + if (virtual_screen.cursor_state) + write_cursor (); + } +} + +static grub_uint16_t +grub_virtual_screen_getwh (void) +{ + return (virtual_screen.columns << 8) | virtual_screen.rows; +} + +static grub_uint16_t +grub_virtual_screen_getxy (void) +{ + return ((virtual_screen.cursor_x << 8) | virtual_screen.cursor_y); +} + +static void +grub_vesafb_gotoxy (grub_uint8_t x, grub_uint8_t y) +{ + if (x >= virtual_screen.columns || y >= virtual_screen.rows) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid point (%u,%u)", + (unsigned) x, (unsigned) y); + return; + } + + if (virtual_screen.cursor_state) + write_char (); + + virtual_screen.cursor_x = x; + virtual_screen.cursor_y = y; + + if (virtual_screen.cursor_state) + write_cursor (); +} + +static void +grub_virtual_screen_cls (void) +{ + grub_uint32_t i; + + for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++) + { + virtual_screen.text_buffer[i].code = ' '; + virtual_screen.text_buffer[i].fg_color = 0; + virtual_screen.text_buffer[i].bg_color = 0; + virtual_screen.text_buffer[i].width = 0; + virtual_screen.text_buffer[i].index = 0; + } + + virtual_screen.cursor_x = virtual_screen.cursor_y = 0; +} + +static void +grub_vesafb_cls (void) +{ + grub_virtual_screen_cls (); + + grub_memset (framebuffer, + 0, + mode_info.y_resolution * bytes_per_scan_line); +} + +static void +grub_virtual_screen_setcolorstate (grub_term_color_state state) +{ + switch (state) + { + case GRUB_TERM_COLOR_STANDARD: + case GRUB_TERM_COLOR_NORMAL: + virtual_screen.fg_color = DEFAULT_FG_COLOR; + virtual_screen.bg_color = DEFAULT_BG_COLOR; + break; + case GRUB_TERM_COLOR_HIGHLIGHT: + virtual_screen.fg_color = DEFAULT_BG_COLOR; + virtual_screen.bg_color = DEFAULT_FG_COLOR; + break; + default: + break; + } +} + +static void +grub_virtual_screen_setcolor (grub_uint8_t normal_color __attribute__ ((unused)), + grub_uint8_t highlight_color __attribute__ ((unused))) +{ + /* FIXME */ +} + +static void +grub_vesafb_setcursor (int on) +{ + if (virtual_screen.cursor_state != on) + { + if (virtual_screen.cursor_state) + write_char (); + else + write_cursor (); + + virtual_screen.cursor_state = on; + } +} + +static struct grub_term grub_vesafb_term = + { + .name = "vesafb", + .init = grub_vesafb_init, + .fini = grub_vesafb_fini, + .putchar = grub_vesafb_putchar, + .checkkey = grub_console_checkkey, + .getkey = grub_console_getkey, + .getwh = grub_virtual_screen_getwh, + .getxy = grub_virtual_screen_getxy, + .gotoxy = grub_vesafb_gotoxy, + .cls = grub_vesafb_cls, + .setcolorstate = grub_virtual_screen_setcolorstate, + .setcolor = grub_virtual_screen_setcolor, + .setcursor = grub_vesafb_setcursor, + .flags = 0, + .next = 0 + }; + +GRUB_MOD_INIT +{ + my_mod = mod; + grub_term_register (&grub_vesafb_term); +} + +GRUB_MOD_FINI +{ + grub_term_unregister (&grub_vesafb_term); +} diff --git a/video/i386/pc/vbe.c b/video/i386/pc/vbe.c new file mode 100644 index 000000000..317aed9c6 --- /dev/null +++ b/video/i386/pc/vbe.c @@ -0,0 +1,392 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Specify "standard" VGA palette, some video cards may + need this and this will also be used when using RGB modes. */ +static struct grub_vbe_palette_data vga_colors[16] = + { + // {B, G, R, A} + {0x00, 0x00, 0x00, 0x00}, // 0 = black + {0xA8, 0x00, 0x00, 0x00}, // 1 = blue + {0x00, 0xA8, 0x00, 0x00}, // 2 = green + {0xA8, 0xA8, 0x00, 0x00}, // 3 = cyan + {0x00, 0x00, 0xA8, 0x00}, // 4 = red + {0xA8, 0x00, 0xA8, 0x00}, // 5 = magenta + {0x00, 0x54, 0xA8, 0x00}, // 6 = brown + {0xA8, 0xA8, 0xA8, 0x00}, // 7 = ligth gray + + {0x54, 0x54, 0x54, 0x00}, // 8 = dark gray + {0xFE, 0x54, 0x54, 0x00}, // 9 = bright blue + {0x54, 0xFE, 0x54, 0x00}, // 10 = bright green + {0xFE, 0xFE, 0x54, 0x00}, // 11 = bright cyan + {0x54, 0x54, 0xFE, 0x00}, // 12 = bright red + {0xFE, 0x54, 0xFE, 0x00}, // 13 = bright magenta + {0x54, 0xFE, 0xFE, 0x00}, // 14 = yellow + {0xFE, 0xFE, 0xFE, 0x00} // 15 = white + }; + +static int vbe_detected = -1; +static int index_color_mode = 0; + +static struct grub_vbe_info_block controller_info; +static struct grub_vbe_mode_info_block active_mode_info; + +static grub_uint32_t active_mode = 0; + +static grub_uint8_t *framebuffer = 0; +static grub_uint32_t bytes_per_scan_line = 0; + +grub_err_t +grub_vbe_probe (struct grub_vbe_info_block *info_block) +{ + struct grub_vbe_info_block *vbe_ib; + grub_uint32_t rc; + + /* Clear caller's controller info block. */ + if (info_block != 0) + { + grub_memset (info_block, 0, sizeof(struct grub_vbe_info_block)); + } + + /* Do not probe more than one time. */ + if (vbe_detected != -1) + { + if (vbe_detected == 1) + { + /* Make copy of controller info block to caller. */ + if (info_block != 0) + { + grub_memcpy (info_block, + &controller_info, + sizeof(struct grub_vbe_info_block)); + } + return GRUB_ERR_NONE; + } + else + { + return GRUB_ERR_BAD_DEVICE; + } + } + + /* Clear old copy of controller info block. */ + grub_memset (&controller_info, 0, sizeof(struct grub_vbe_info_block)); + + /* Mark VESA BIOS extension as undetected. */ + vbe_detected = 0; + + /* Use low memory scratch area as temporary storage for VESA BIOS call. */ + vbe_ib = (struct grub_vbe_info_block *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; + + /* Prepare info block. */ + grub_memset (vbe_ib, 0, sizeof(struct grub_vbe_info_block)); + + vbe_ib->signature[0] = 'V'; + vbe_ib->signature[1] = 'B'; + vbe_ib->signature[2] = 'E'; + vbe_ib->signature[3] = '2'; + + /* Try to get controller info block. */ + rc = grub_vbe_get_controller_info (vbe_ib); + if (rc != 0x004F) + { + return GRUB_ERR_BAD_DEVICE; + } + + /* Copy it for later usage. */ + grub_memcpy (&controller_info, + vbe_ib, + sizeof(struct grub_vbe_info_block)); + + /* Copy it for caller. */ + grub_memcpy (info_block, + vbe_ib, + sizeof(struct grub_vbe_info_block)); + + /* Mark VESA BIOS extension as detected. */ + vbe_detected = 1; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_vbe_set_video_mode (grub_uint32_t mode, + struct grub_vbe_mode_info_block *mode_info) +{ + grub_uint32_t rc; + + /* If grub_vesafb_probe has not been called or no VBE, abort. */ + if (vbe_detected == 0) + { + return GRUB_ERR_BAD_DEVICE; + } + + /* Try to get mode info. */ + rc = grub_vbe_get_video_mode_info (mode, &active_mode_info); + if (rc != GRUB_ERR_NONE) + { + return rc; + } + + /* For all VESA BIOS modes, force linear frame buffer. */ + if (mode >= 0x100) + { + /* We only want linear frame buffer modes. */ + mode |= 1 << 14; + + /* Determine frame buffer pixel format. */ + switch(active_mode_info.memory_model) + { + case 0x04: + index_color_mode = 1; + break; + + case 0x06: + index_color_mode = 0; + break; + + default: + return GRUB_ERR_BAD_DEVICE; + } + } + + /* Try to set video mode. */ + rc = grub_vbe_set_mode (mode, 0); + if (rc != 0x004F) + { + return GRUB_ERR_BAD_DEVICE; + } + + /* Save information for later usage. */ + active_mode = mode; + + if (mode < 0x100) + { + /* If this is not a VESA mode, guess address. */ + framebuffer = (grub_uint8_t *)0xA0000; + index_color_mode = 1; + } + else + { + framebuffer = (grub_uint8_t *)active_mode_info.phys_base_addr; + + if (controller_info.version >= 0x300) + { + bytes_per_scan_line = active_mode_info.lin_bytes_per_scan_line; + } + else + { + bytes_per_scan_line = active_mode_info.bytes_per_scan_line; + } + } + + /* If video mode is in indexed color, setup default VGA palette. */ + if (index_color_mode != 0) + { + rc = grub_vbe_set_palette_data (16, 0, vga_colors); + if (rc != 0x004F) + { + return GRUB_ERR_BAD_DEVICE; + } + } + + /* Copy mode info for caller. */ + if (mode_info != 0) + { + grub_memcpy (mode_info, + &active_mode_info, + sizeof(struct grub_vbe_mode_info_block)); + } + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_vbe_get_video_mode (grub_uint32_t *mode) +{ + grub_uint32_t rc; + + /* If grub_vesafb_probe has not been called or no VBE, abort. */ + if (vbe_detected == 0) + { + return GRUB_ERR_BAD_DEVICE; + } + + /* Try to query current mode from VESA BIOS. */ + rc = grub_vbe_get_mode (mode); + if (rc != 0x004F) + { + return GRUB_ERR_BAD_DEVICE; + } + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_vbe_get_video_mode_info (grub_uint32_t mode, + struct grub_vbe_mode_info_block *mode_info) +{ + struct grub_vbe_mode_info_block *mi_tmp = (struct grub_vbe_mode_info_block *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; + grub_uint32_t rc; + + /* If grub_vesafb_probe has not been called or no VBE, abort. */ + if (vbe_detected == 0) + { + return GRUB_ERR_BAD_DEVICE; + } + + /* If mode is not VESA mode, skip mode info query. */ + if (mode >= 0x100) + { + /* Try to get mode info from VESA BIOS. */ + rc = grub_vbe_get_mode_info (mode, mi_tmp); + if (rc != 0x004F) + { + return GRUB_ERR_BAD_DEVICE; + } + + /* Make copy of mode info block. */ + grub_memcpy (mode_info, + mi_tmp, + sizeof(struct grub_vbe_mode_info_block)); + } + else + { + /* Just clear mode info block if it isn't a VESA mode. */ + grub_memset (mode_info, + 0, + sizeof(struct grub_vbe_mode_info_block)); + } + + return GRUB_ERR_NONE; +} + +void +grub_vbe_set_pixel_rgb (grub_uint32_t x, + grub_uint32_t y, + grub_uint8_t red, + grub_uint8_t green, + grub_uint8_t blue) +{ + grub_uint32_t value; + + if (x >= active_mode_info.x_resolution) + return; + + if (y >= active_mode_info.y_resolution) + return; + + if (controller_info.version >= 0x300) + { + red >>= 8 - active_mode_info.lin_red_mask_size; + green >>= 8 - active_mode_info.lin_green_mask_size; + blue >>= 8 - active_mode_info.lin_blue_mask_size; + + value = red << active_mode_info.lin_red_field_position; + value |= green << active_mode_info.lin_green_field_position; + value |= blue << active_mode_info.lin_blue_field_position; + } + else + { + red >>= 8 - active_mode_info.red_mask_size; + green >>= 8 - active_mode_info.green_mask_size; + blue >>= 8 - active_mode_info.blue_mask_size; + + value = red << active_mode_info.red_field_position; + value |= green << active_mode_info.green_field_position; + value |= blue << active_mode_info.blue_field_position; + } + + if (active_mode_info.bits_per_pixel == 32) + { + grub_uint32_t *ptr = (grub_uint32_t *)(framebuffer + y * bytes_per_scan_line + x * 4); + + *ptr = value; + } + else if (active_mode_info.bits_per_pixel == 24) + { + grub_uint8_t *ptr = (grub_uint8_t *)(framebuffer + y * bytes_per_scan_line + x * 3); + grub_uint8_t *ptr2 = (grub_uint8_t *)&value; + + ptr[0] = ptr2[0]; + ptr[1] = ptr2[1]; + ptr[2] = ptr2[2]; + } + else if ((active_mode_info.bits_per_pixel == 16) || + (active_mode_info.bits_per_pixel == 15)) + { + grub_uint16_t *ptr = (grub_uint16_t *)(framebuffer + y * bytes_per_scan_line + x * 2); + + *ptr = (grub_uint16_t)(value & 0xFFFF); + } +} + +void +grub_vbe_set_pixel_index (grub_uint32_t x, + grub_uint32_t y, + grub_uint8_t color) +{ + if (x >= active_mode_info.x_resolution) + return; + + if (y >= active_mode_info.y_resolution) + return; + + if (index_color_mode == 1) + { + grub_uint8_t *ptr = (grub_uint8_t *)(framebuffer + y * bytes_per_scan_line + x); + + *ptr = color; + } + else + { + color &= 0x0F; + + if (color < 16) + { + grub_vbe_set_pixel_rgb (x, + y, + vga_colors[color].red, + vga_colors[color].green, + vga_colors[color].blue); + } + else + { + grub_vbe_set_pixel_rgb (x, + y, + 0, + 0, + 0); + } + } +}