summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2009-11-13 20:16:43 -0800
committerH. Peter Anvin <hpa@zytor.com>2009-11-13 20:22:44 -0800
commit7b04ddff50bc4903d50ed570daf01422ebfaa6ed (patch)
tree94dc55c891e861e9fcae8b2ca417d7cefe3fa999
parent56e6eae7f4af352aaf33840fd652474098486ea6 (diff)
downloadsyslinux-7b04ddff50bc4903d50ed570daf01422ebfaa6ed.tar.gz
mboot.c32: add VESA mode setting supportsyslinux-3.84-pre2
Add VESA mode setting support per the Multiboot specification. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--com32/mboot/Makefile2
-rw-r--r--com32/mboot/initvesa.c219
-rw-r--r--com32/mboot/map.c32
-rw-r--r--com32/mboot/mboot.c7
-rw-r--r--com32/mboot/mboot.h6
-rw-r--r--com32/mboot/vesa.h100
6 files changed, 347 insertions, 19 deletions
diff --git a/com32/mboot/Makefile b/com32/mboot/Makefile
index a6902716..c1ac76fb 100644
--- a/com32/mboot/Makefile
+++ b/com32/mboot/Makefile
@@ -24,7 +24,7 @@ LNXLIBS = ../libutil/libutil_lnx.a
MODULES = mboot.c32
TESTFILES =
-OBJS = mboot.o map.o mem.o apm.o solaris.o
+OBJS = mboot.o map.o mem.o initvesa.o apm.o solaris.o
all: $(MODULES) $(TESTFILES)
diff --git a/com32/mboot/initvesa.c b/com32/mboot/initvesa.c
new file mode 100644
index 00000000..5452648d
--- /dev/null
+++ b/com32/mboot/initvesa.c
@@ -0,0 +1,219 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * initvesa.c
+ *
+ * Query the VESA BIOS and select a 640x480x32 mode with local mapping
+ * support, if one exists.
+ */
+
+#include <inttypes.h>
+#include <com32.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "vesa.h"
+#include "mboot.h"
+
+struct vesa_info vesa_info;
+
+void set_graphics_mode(const struct multiboot_header *mbh,
+ struct multiboot_info *mbi)
+{
+ com32sys_t rm;
+ uint16_t mode, bestmode, *mode_ptr;
+ struct vesa_general_info *gi;
+ struct vesa_mode_info *mi;
+ int pxf, bestpxf;
+ int wantx, wanty;
+ int err, besterr;
+ bool better;
+ addr_t viaddr;
+
+ /* Only do this if requested by the OS image */
+ if (!(mbh->flags & MULTIBOOT_VIDEO_MODE) || mbh->mode_type != 0)
+ return;
+
+ /* Allocate space in the bounce buffer for these structures */
+ gi = &((struct vesa_info *)__com32.cs_bounce)->gi;
+ mi = &((struct vesa_info *)__com32.cs_bounce)->mi;
+
+ memset(&rm, 0, sizeof rm);
+ memset(gi, 0, sizeof *gi);
+
+ gi->signature = VBE2_MAGIC; /* Get VBE2 extended data */
+ rm.eax.w[0] = 0x4F00; /* Get SVGA general information */
+ rm.edi.w[0] = OFFS(gi);
+ rm.es = SEG(gi);
+ __intcall(0x10, &rm, &rm);
+
+ if (rm.eax.w[0] != 0x004F)
+ return; /* Function call failed */
+ if (gi->signature != VESA_MAGIC)
+ return; /* No magic */
+ if (gi->version < 0x0102)
+ return; /* VESA 1.2+ required */
+
+ memcpy(&vesa_info.gi, gi, sizeof *gi);
+
+ /* Search for a suitable mode with a suitable color and memory model... */
+
+ mode_ptr = GET_PTR(gi->video_mode_ptr);
+ bestmode = 0;
+ bestpxf = 0;
+ wantx = mbh->width ? mbh->width : 0xffff;
+ wanty = mbh->height ? mbh->height : 0xffff;
+ besterr = wantx + wanty;
+
+ while ((mode = *mode_ptr++) != 0xFFFF) {
+ mode &= 0x1FF; /* The rest are attributes of sorts */
+
+ memset(mi, 0, sizeof *mi);
+ rm.eax.w[0] = 0x4F01; /* Get SVGA mode information */
+ rm.ecx.w[0] = mode;
+ rm.edi.w[0] = OFFS(mi);
+ rm.es = SEG(mi);
+ __intcall(0x10, &rm, &rm);
+
+ /* Must be a supported mode */
+ if (rm.eax.w[0] != 0x004f)
+ continue;
+
+ /* Must be an LFB color graphics mode supported by the hardware.
+
+ The bits tested are:
+ 7 - linear frame buffer
+ 4 - graphics mode
+ 3 - color mode
+ 1 - mode information available (mandatory in VBE 1.2+)
+ 0 - mode supported by hardware
+ */
+ if ((mi->mode_attr & 0x009b) != 0x009b)
+ continue;
+
+ /* We don't support multibank (interlaced memory) modes */
+ /*
+ * Note: The Bochs VESA BIOS (vbe.c 1.58 2006/08/19) violates the
+ * specification which states that banks == 1 for unbanked modes;
+ * fortunately it does report bank_size == 0 for those.
+ */
+ if (mi->banks > 1 && mi->bank_size)
+ continue;
+
+ /* Must either be a packed-pixel mode or a direct color mode
+ (depending on VESA version ); must be a supported pixel format */
+ pxf = 0; /* Not usable */
+
+ if (mi->bpp == 32 &&
+ (mi->memory_layout == 4 ||
+ (mi->memory_layout == 6 && mi->rpos == 16 && mi->gpos == 8 &&
+ mi->bpos == 0)))
+ pxf = 32;
+ else if (mi->bpp == 24 &&
+ (mi->memory_layout == 4 ||
+ (mi->memory_layout == 6 && mi->rpos == 16 && mi->gpos == 8 &&
+ mi->bpos == 0)))
+ pxf = 24;
+ else if (mi->bpp == 16 &&
+ (mi->memory_layout == 4 ||
+ (mi->memory_layout == 6 && mi->rpos == 11 && mi->gpos == 5 &&
+ mi->bpos == 0)))
+ pxf = 16;
+ else if (mi->bpp == 15 &&
+ (mi->memory_layout == 4 ||
+ (mi->memory_layout == 6 && mi->rpos == 10 && mi->gpos == 5 &&
+ mi->bpos == 0)))
+ pxf = 15;
+
+ better = false;
+
+ err = abs(mi->h_res - wantx) + abs(mi->v_res - wanty);
+
+ if (!bestpxf)
+ better = true;
+ else if ((vesa_info.mi.h_res < wantx || vesa_info.mi.v_res < wantx) &&
+ mi->h_res >= wantx && mi->v_res >= wanty)
+ /* This matches criteria, which the previous one didn't */
+ better = true;
+ else if (err < besterr)
+ better = true;
+ else if (err == besterr && (pxf == (int)mbh->depth || pxf > bestpxf))
+ better = true;
+
+ if (better) {
+ bestmode = mode;
+ bestpxf = pxf;
+ memcpy(&vesa_info.mi, mi, sizeof *mi);
+ }
+ }
+
+ if (!bestpxf)
+ return; /* No mode found */
+
+ mi = &vesa_info.mi;
+ mode = bestmode;
+
+ /* Now set video mode */
+ rm.eax.w[0] = 0x4F02; /* Set SVGA video mode */
+ mode |= 0x4000; /* Request linear framebuffer */
+ rm.ebx.w[0] = mode;
+ __intcall(0x10, &rm, &rm);
+ if (rm.eax.w[0] != 0x004F)
+ return; /* Failed to set mode */
+
+ mbi->flags |= MB_INFO_VIDEO_INFO;
+ mbi->vbe_mode = mode;
+ viaddr = map_data(&vesa_info, sizeof vesa_info, 4, 0);
+ mbi->vbe_control_info = viaddr + offsetof(struct vesa_info, gi);
+ mbi->vbe_mode_info = viaddr + offsetof(struct vesa_info, mi);
+
+ /* Get the VBE 2.x PM entry point if supported */
+ rm.eax.w[0] = 0x4F0A;
+ rm.ebx.w[0] = 0;
+ __intcall(0x10, &rm, &rm);
+ if (rm.eax.w[0] == 0x004F) {
+ mbi->vbe_interface_seg = rm.es;
+ mbi->vbe_interface_off = rm.edi.w[0];
+ mbi->vbe_interface_len = rm.ecx.w[0];
+ }
+
+ /* Tell syslinux we changed video mode */
+ rm.eax.w[0] = 0x0017; /* Report video mode change */
+ /* In theory this should be:
+
+ rm.ebx.w[0] = (mi->mode_attr & 4) ? 0x0007 : 0x000f;
+
+ However, that would assume all systems that claim to handle text
+ output in VESA modes actually do that... */
+ rm.ebx.w[0] = 0x000f;
+ rm.ecx.w[0] = vesa_info.mi.h_res;
+ rm.edx.w[0] = vesa_info.mi.v_res;
+ __intcall(0x22, &rm, NULL);
+}
diff --git a/com32/mboot/map.c b/com32/mboot/map.c
index 887776fe..a32f9b3b 100644
--- a/com32/mboot/map.c
+++ b/com32/mboot/map.c
@@ -36,7 +36,6 @@
static struct syslinux_movelist *ml = NULL;
static struct syslinux_memmap *mmap = NULL, *amap = NULL;
-static struct multiboot_header *mbh;
static addr_t mboot_high_water_mark = 0x100000;
/*
@@ -100,8 +99,9 @@ int init_map(void)
return 0;
}
-int map_image(void *ptr, size_t len)
+struct multiboot_header *map_image(void *ptr, size_t len)
{
+ struct multiboot_header *mbh;
int mbh_len;
char *cptr = ptr;
Elf32_Ehdr *eh = ptr;
@@ -134,10 +134,10 @@ int map_image(void *ptr, size_t len)
}
if (mbh_len) {
- bad_flags = mbh->flags & (MULTIBOOT_UNSUPPORTED | MULTIBOOT_VIDEO_MODE);
+ bad_flags = mbh->flags & MULTIBOOT_UNSUPPORTED;
if (bad_flags) {
printf("Unsupported Multiboot flags set: %#x\n", bad_flags);
- return -1;
+ return NULL;
}
}
@@ -187,13 +187,13 @@ int map_image(void *ptr, size_t len)
printf
("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
addr, msize);
- return -1; /* Memory region unavailable */
+ return NULL; /* Memory region unavailable */
}
/* Mark this region as allocated in the available map */
if (syslinux_add_memmap(&amap, addr, msize, SMT_ALLOC)) {
error("Overlapping segments found in ELF header\n");
- return -1;
+ return NULL;
}
if (ph->p_filesz) {
@@ -201,7 +201,7 @@ int map_image(void *ptr, size_t len)
if (syslinux_add_movelist
(&ml, addr, (addr_t) cptr + ph->p_offset, dsize)) {
error("Failed to map PHDR data\n");
- return -1;
+ return NULL;
}
}
if (msize > dsize) {
@@ -209,7 +209,7 @@ int map_image(void *ptr, size_t len)
if (syslinux_add_memmap
(&mmap, addr + dsize, msize - dsize, SMT_ZERO)) {
error("Failed to map PHDR zero region\n");
- return -1;
+ return NULL;
}
}
if (addr + msize > mboot_high_water_mark)
@@ -235,7 +235,7 @@ int map_image(void *ptr, size_t len)
addr = map_data(sh, len, 4096, MAP_HIGH | MAP_NOPAD);
if (!addr) {
error("Failed to map symbol table\n");
- return -1;
+ return NULL;
}
mbinfo.flags |= MB_INFO_ELF_SHDR;
@@ -257,7 +257,7 @@ int map_image(void *ptr, size_t len)
align, MAP_HIGH);
if (!addr) {
error("Failed to map symbol section\n");
- return -1;
+ return NULL;
}
sh[i].sh_addr = addr;
}
@@ -279,34 +279,34 @@ int map_image(void *ptr, size_t len)
!= SMT_FREE) {
printf("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
mbh->load_addr, data_len + bss_len);
- return -1; /* Memory region unavailable */
+ return NULL; /* Memory region unavailable */
}
if (syslinux_add_memmap(&amap, mbh->load_addr,
data_len + bss_len, SMT_ALLOC)) {
error("Failed to claim a.out address space!\n");
- return -1;
+ return NULL;
}
if (data_len)
if (syslinux_add_movelist(&ml, mbh->load_addr, (addr_t) data_ptr,
data_len)) {
error("Failed to map a.out data\n");
- return -1;
+ return NULL;
}
if (bss_len)
if (syslinux_add_memmap
(&mmap, mbh->load_end_addr, bss_len, SMT_ZERO)) {
error("Failed to map a.out bss\n");
- return -1;
+ return NULL;
}
if (mbh->bss_end_addr > mboot_high_water_mark)
mboot_high_water_mark = mbh->bss_end_addr;
} else {
error
("Invalid Multiboot image: neither ELF header nor a.out kludge found\n");
- return -1;
+ return NULL;
}
- return 0;
+ return mbh;
}
/*
diff --git a/com32/mboot/mboot.c b/com32/mboot/mboot.c
index e7bb8dbb..8425e068 100644
--- a/com32/mboot/mboot.c
+++ b/com32/mboot/mboot.c
@@ -152,6 +152,7 @@ int main(int argc, char *argv[])
{
int nmodules;
struct module_data *modules;
+ struct multiboot_header *mbh;
bool keeppxe = false;
openconsole(&dev_null_r, &dev_stdcon_w);
@@ -193,7 +194,8 @@ int main(int argc, char *argv[])
* Map the primary image. This should be done before mapping anything
* else, since it will have fixed address requirements.
*/
- if (map_image(modules[0].data, modules[0].len))
+ mbh = map_image(modules[0].data, modules[0].len);
+ if (!mbh)
return 1;
/* Map the mbinfo structure */
@@ -223,6 +225,9 @@ int main(int argc, char *argv[])
if (opt.solaris)
mboot_solaris_dhcp_hack();
+ /* Set the graphics mode if requested */
+ set_graphics_mode(mbh, &mbinfo);
+
/* Run it */
mboot_run(keeppxe ? 3 : 0);
error("mboot.c32: boot failed\n");
diff --git a/com32/mboot/mboot.h b/com32/mboot/mboot.h
index e5786206..993b31a8 100644
--- a/com32/mboot/mboot.h
+++ b/com32/mboot/mboot.h
@@ -79,7 +79,7 @@ extern struct my_options {
#define MAP_NOPAD 2
addr_t map_data(const void *data, size_t len, size_t align, int flags);
addr_t map_string(const char *string);
-int map_image(void *ptr, size_t len);
+struct multiboot_header *map_image(void *ptr, size_t len);
void mboot_run(int bootflags);
int init_map(void);
@@ -92,4 +92,8 @@ void mboot_apm(void);
/* solaris.c */
void mboot_solaris_dhcp_hack(void);
+/* initvesa.c */
+void set_graphics_mode(const struct multiboot_header *mbh,
+ struct multiboot_info *mbi);
+
#endif /* MBOOT_H */
diff --git a/com32/mboot/vesa.h b/com32/mboot/vesa.h
new file mode 100644
index 00000000..ecc084aa
--- /dev/null
+++ b/com32/mboot/vesa.h
@@ -0,0 +1,100 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+#ifndef LIB_SYS_VESA_H
+#define LIB_SYS_VESA_H
+
+#include <inttypes.h>
+#include <com32.h>
+
+/* VESA General Information table */
+struct vesa_general_info {
+ uint32_t signature; /* Magic number = "VESA" */
+ uint16_t version;
+ far_ptr_t vendor_string;
+ uint8_t capabilities[4];
+ far_ptr_t video_mode_ptr;
+ uint16_t total_memory;
+
+ uint16_t oem_software_rev;
+ far_ptr_t oem_vendor_name_ptr;
+ far_ptr_t oem_product_name_ptr;
+ far_ptr_t oem_product_rev_ptr;
+
+ uint8_t reserved[222];
+ uint8_t oem_data[256];
+} __attribute__ ((packed));
+
+#define VESA_MAGIC ('V' + ('E' << 8) + ('S' << 16) + ('A' << 24))
+#define VBE2_MAGIC ('V' + ('B' << 8) + ('E' << 16) + ('2' << 24))
+
+struct vesa_mode_info {
+ uint16_t mode_attr;
+ uint8_t win_attr[2];
+ uint16_t win_grain;
+ uint16_t win_size;
+ uint16_t win_seg[2];
+ far_ptr_t win_scheme;
+ uint16_t logical_scan;
+
+ uint16_t h_res;
+ uint16_t v_res;
+ uint8_t char_width;
+ uint8_t char_height;
+ uint8_t memory_planes;
+ uint8_t bpp;
+ uint8_t banks;
+ uint8_t memory_layout;
+ uint8_t bank_size;
+ uint8_t image_pages;
+ uint8_t page_function;
+
+ uint8_t rmask;
+ uint8_t rpos;
+ uint8_t gmask;
+ uint8_t gpos;
+ uint8_t bmask;
+ uint8_t bpos;
+ uint8_t resv_mask;
+ uint8_t resv_pos;
+ uint8_t dcm_info;
+
+ uint8_t *lfb_ptr; /* Linear frame buffer address */
+ uint8_t *offscreen_ptr; /* Offscreen memory address */
+ uint16_t offscreen_size;
+
+ uint8_t reserved[206];
+} __attribute__ ((packed));
+
+struct vesa_info {
+ struct vesa_general_info gi;
+ struct vesa_mode_info mi;
+};
+
+extern struct vesa_info vesa_info;
+
+#endif /* LIB_SYS_VESA_H */