From: H. Peter Anvin Date: Sat, 14 Nov 2009 04:16:43 +0000 (-0800) Subject: mboot.c32: add VESA mode setting support X-Git-Tag: syslinux-3.84-pre2^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=7b04ddff50bc4903d50ed570daf01422ebfaa6ed;p=profile%2Fivi%2Fsyslinux.git mboot.c32: add VESA mode setting support Add VESA mode setting support per the Multiboot specification. Signed-off-by: H. Peter Anvin --- diff --git a/com32/mboot/Makefile b/com32/mboot/Makefile index a690271..c1ac76f 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 0000000..5452648 --- /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 +#include +#include +#include +#include + +#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 887776f..a32f9b3 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 e7bb8db..8425e06 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 e578620..993b31a 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 0000000..ecc084a --- /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 +#include + +/* 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 */