--- /dev/null
+#ifndef _IPXE_VESAFB_H
+#define _IPXE_VESAFB_H
+
+/** @file
+ *
+ * VESA frame buffer console
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <realmode.h>
+
+/** INT 10,4f00: return controller information */
+#define VBE_CONTROLLER_INFO 0x4f00
+
+/** VBE controller information */
+struct vbe_controller_info {
+ /** VBE signature */
+ uint32_t vbe_signature;
+ /** VBE minor version */
+ uint8_t vbe_minor_version;
+ /** VBE major version */
+ uint8_t vbe_major_version;
+ /** Pointer to OEM string */
+ struct segoff oem_string_ptr;
+ /** Capabilities of graphics controller */
+ uint32_t capabilities;
+ /** Pointer to video mode list */
+ struct segoff video_mode_ptr;
+ /** Number of 64kB memory blocks */
+ uint16_t total_memory;
+ /** VBE implementation software revision */
+ uint16_t oem_software_rev;
+ /** Pointer to vendor name string */
+ struct segoff oem_vendor_name_ptr;
+ /** Pointer to product name string */
+ struct segoff oem_product_name_ptr;
+ /** Pointer to product revision string */
+ struct segoff oem_product_rev_ptr;
+ /** Reserved for VBE implementation scratch area */
+ uint8_t reserved[222];
+ /* VBE2.0 defines an additional 256-byte data area for
+ * including the OEM strings inline within the VBE information
+ * block; we omit this to reduce the amount of base memory
+ * required for VBE calls.
+ */
+} __attribute__ (( packed ));
+
+/** VBE controller information signature */
+#define VBE_CONTROLLER_SIGNATURE \
+ ( ( 'V' << 0 ) | ( 'E' << 8 ) | ( 'S' << 16 ) | ( 'A' << 24 ) )
+
+/** VBE mode list end marker */
+#define VBE_MODE_END 0xffff
+
+/** INT 10,4f01: return VBE mode information */
+#define VBE_MODE_INFO 0x4f01
+
+/** VBE mode information */
+struct vbe_mode_info {
+ /** Mode attributes */
+ uint16_t mode_attributes;
+ /** Window A attributes */
+ uint8_t win_a_attributes;
+ /** Window B attributes */
+ uint8_t win_b_attributes;
+ /** Window granularity */
+ uint16_t win_granularity;
+ /** Window size */
+ uint16_t win_size;
+ /** Window A start segment */
+ uint16_t win_a_segment;
+ /** Window B start segment */
+ uint16_t win_b_segment;
+ /** Pointer to window function */
+ struct segoff win_func_ptr;
+ /** Bytes per scan line */
+ uint16_t bytes_per_scan_line;
+ /** Horizontal resolution in pixels or characters */
+ uint16_t x_resolution;
+ /** Vertical resolution in pixels or characters */
+ uint16_t y_resolution;
+ /** Character cell width in pixels */
+ uint8_t x_char_size;
+ /** Character cell height in pixels */
+ uint8_t y_char_size;
+ /** Number of memory planes */
+ uint8_t number_of_planes;
+ /** Bits per pixel */
+ uint8_t bits_per_pixel;
+ /** Number of banks */
+ uint8_t number_of_banks;
+ /** Memory model type */
+ uint8_t memory_model;
+ /** Bank size in kB */
+ uint8_t bank_size;
+ /** Number of images */
+ uint8_t number_of_image_pages;
+ /** Reserved for page function */
+ uint8_t reserved_1;
+ /** Size of direct colour red mask in bits */
+ uint8_t red_mask_size;
+ /** Bit position of LSB of red mask */
+ uint8_t red_field_position;
+ /** Size of direct colour green mask in bits */
+ uint8_t green_mask_size;
+ /** Bit position of LSB of green mask */
+ uint8_t green_field_position;
+ /** Size of direct colour blue mask in bits */
+ uint8_t blue_mask_size;
+ /** Bit position of LSB of blue mask */
+ uint8_t blue_field_position;
+ /** Size of direct colour reserved mask in bits */
+ uint8_t rsvd_mask_size;
+ /** Bit position of LSB of reserved mask */
+ uint8_t rsvd_field_position;
+ /** Direct colour mode attributes */
+ uint8_t direct_colour_mode_info;
+ /** Physical address for flat memory frame buffer */
+ uint32_t phys_base_ptr;
+ /** Pointer to start of off-screen memory */
+ uint32_t off_screen_mem_offset;
+ /** Amount of off-screen memory in 1kB units */
+ uint16_t off_screen_mem_size;
+ /** Reserved */
+ uint8_t reserved_2[206];
+} __attribute__ (( packed ));
+
+/** VBE mode attributes */
+enum vbe_mode_attributes {
+ /** Mode supported in hardware */
+ VBE_MODE_ATTR_SUPPORTED = 0x0001,
+ /** TTY output functions supported by BIOS */
+ VBE_MODE_ATTR_TTY = 0x0004,
+ /** Colour mode */
+ VBE_MODE_ATTR_COLOUR = 0x0008,
+ /** Graphics mode */
+ VBE_MODE_ATTR_GRAPHICS = 0x0010,
+ /** Not a VGA compatible mode */
+ VBE_MODE_ATTR_NOT_VGA = 0x0020,
+ /** VGA compatible windowed memory mode is not available */
+ VBE_MODE_ATTR_NOT_WINDOWED = 0x0040,
+ /** Linear frame buffer mode is available */
+ VBE_MODE_ATTR_LINEAR = 0x0080,
+ /** Double scan mode is available */
+ VBE_MODE_ATTR_DOUBLE = 0x0100,
+ /** Interlaced mode is available */
+ VBE_MODE_ATTR_INTERLACED = 0x0200,
+ /** Hardware triple buffering support */
+ VBE_MODE_ATTR_TRIPLE_BUF = 0x0400,
+ /** Hardware stereoscopic display support */
+ VBE_MODE_ATTR_STEREO = 0x0800,
+ /** Dual display start address support */
+ VBE_MODE_ATTR_DUAL = 0x1000,
+};
+
+/** VBE mode memory models */
+enum vbe_mode_memory_model {
+ /** Text mode */
+ VBE_MODE_MODEL_TEXT = 0x00,
+ /** CGA graphics mode */
+ VBE_MODE_MODEL_CGA = 0x01,
+ /** Hercules graphics mode */
+ VBE_MODE_MODEL_HERCULES = 0x02,
+ /** Planar mode */
+ VBE_MODE_MODEL_PLANAR = 0x03,
+ /** Packed pixel mode */
+ VBE_MODE_MODEL_PACKED_PIXEL = 0x04,
+ /** Non-chain 4, 256 colour mode */
+ VBE_MODE_MODEL_NON_CHAIN_4 = 0x05,
+ /** Direct colour mode */
+ VBE_MODE_MODEL_DIRECT_COLOUR = 0x06,
+ /** YUV mode */
+ VBE_MODE_MODEL_YUV = 0x07,
+};
+
+/** INT 10,4f02: set VBE mode */
+#define VBE_SET_MODE 0x4f02
+
+/** VBE linear frame buffer mode bit */
+#define VBE_MODE_LINEAR 0x4000
+
+/** INT 10,1130: get font information */
+#define VBE_GET_FONT 0x1130
+
+/** Font sets */
+enum vbe_font_set {
+ /** 8x14 character font */
+ VBE_FONT_8x14 = 0x0200,
+ /** 8x8 double dot font */
+ VBE_FONT_8x8_DOUBLE = 0x0300,
+ /** 8x8 double dot font (high 128 characters) */
+ VBE_FONT_8x8_DOUBLE_HIGH = 0x0400,
+ /** 9x14 alpha alternate font */
+ VBE_FONT_9x14_ALPHA_ALT = 0x0500,
+ /** 8x16 font */
+ VBE_FONT_8x16 = 0x0600,
+ /** 9x16 alternate font */
+ VBE_FONT_9x16_ALT = 0x0700,
+};
+
+/** INT 10,00: set VGA mode */
+#define VBE_SET_VGA_MODE 0x0000
+
+/** INT 10,0f: get VGA mode */
+#define VBE_GET_VGA_MODE 0x0f00
+
+#endif /* _IPXE_VESAFB_H */
--- /dev/null
+/*
+ * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/** @file
+ *
+ * VESA frame buffer console
+ *
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <realmode.h>
+#include <ipxe/console.h>
+#include <ipxe/io.h>
+#include <ipxe/fbcon.h>
+#include <ipxe/vesafb.h>
+#include <config/console.h>
+
+/* Avoid dragging in BIOS console if not otherwise used */
+extern struct console_driver bios_console;
+struct console_driver bios_console __attribute__ (( weak ));
+
+/* Disambiguate the various error causes */
+#define EIO_FAILED __einfo_error ( EINFO_EIO_FAILED )
+#define EINFO_EIO_FAILED \
+ __einfo_uniqify ( EINFO_EIO, 0x01, \
+ "Function call failed" )
+#define EIO_HARDWARE __einfo_error ( EINFO_EIO_HARDWARE )
+#define EINFO_EIO_HARDWARE \
+ __einfo_uniqify ( EINFO_EIO, 0x02, \
+ "Not supported in current configuration" )
+#define EIO_MODE __einfo_error ( EINFO_EIO_MODE )
+#define EINFO_EIO_MODE \
+ __einfo_uniqify ( EINFO_EIO, 0x03, \
+ "Invalid in current video mode" )
+#define EIO_VBE( code ) \
+ EUNIQ ( EINFO_EIO, (code), EIO_FAILED, EIO_HARDWARE, EIO_MODE )
+
+/* Set default console usage if applicable */
+#if ! ( defined ( CONSOLE_VESAFB ) && CONSOLE_EXPLICIT ( CONSOLE_VESAFB ) )
+#undef CONSOLE_VESAFB
+#define CONSOLE_VESAFB ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
+#endif
+
+/** Font corresponding to selected character width and height */
+#define VESAFB_FONT VBE_FONT_8x16
+
+/* Forward declaration */
+struct console_driver vesafb_console __console_driver;
+
+/** A VESA frame buffer */
+struct vesafb {
+ /** Frame buffer console */
+ struct fbcon fbcon;
+ /** Pixel geometry */
+ struct fbcon_geometry pixel;
+ /** Colour mapping */
+ struct fbcon_colour_map map;
+ /** Font definition */
+ struct fbcon_font font;
+ /** Total length */
+ size_t len;
+ /** Saved VGA mode */
+ uint8_t saved_mode;
+};
+
+/** The VESA frame buffer */
+static struct vesafb vesafb;
+
+/** Base memory buffer used for VBE calls */
+union vbe_buffer {
+ /** VBE controller information block */
+ struct vbe_controller_info controller;
+ /** VBE mode information block */
+ struct vbe_mode_info mode;
+};
+static union vbe_buffer __bss16 ( vbe_buf );
+#define vbe_buf __use_data16 ( vbe_buf )
+
+/**
+ * Convert VBE status code to iPXE status code
+ *
+ * @v status VBE status code
+ * @ret rc Return status code
+ */
+static int vesafb_rc ( unsigned int status ) {
+ unsigned int code;
+
+ if ( ( status & 0xff ) != 0x4f )
+ return -ENOTSUP;
+ code = ( ( status >> 8 ) & 0xff );
+ return ( code ? -EIO_VBE ( code ) : 0 );
+}
+
+/**
+ * Get font definition
+ *
+ */
+static void vesafb_font ( void ) {
+ struct segoff font;
+
+ /* Get font information
+ *
+ * Working around gcc bugs is icky here. The value we want is
+ * returned in %ebp, but there's no way to specify %ebp in an
+ * output constraint. We can't put %ebp in the clobber list,
+ * because this tends to cause random build failures on some
+ * gcc versions. We can't manually push/pop %ebp and return
+ * the value via a generic register output constraint, because
+ * gcc might choose to use %ebp to satisfy that constraint
+ * (and we have no way to prevent it from so doing).
+ *
+ * Work around this hideous mess by using %ecx and %edx as the
+ * output registers, since they get clobbered anyway.
+ */
+ __asm__ __volatile__ ( REAL_CODE ( "pushw %%bp\n\t" /* gcc bug */
+ "int $0x10\n\t"
+ "movw %%es, %%cx\n\t"
+ "movw %%bp, %%dx\n\t"
+ "popw %%bp\n\t" /* gcc bug */ )
+ : "=c" ( font.segment ),
+ "=d" ( font.offset )
+ : "a" ( VBE_GET_FONT ),
+ "b" ( VESAFB_FONT ) );
+ DBGC ( &vbe_buf, "VESAFB has font %04x at %04x:%04x\n",
+ VESAFB_FONT, font.segment, font.offset );
+ vesafb.font.start = real_to_user ( font.segment, font.offset );
+}
+
+/**
+ * Get VBE mode list
+ *
+ * @ret mode_numbers Mode number list (terminated with VBE_MODE_END)
+ * @ret rc Return status code
+ *
+ * The caller is responsible for eventually freeing the mode list.
+ */
+static int vesafb_mode_list ( uint16_t **mode_numbers ) {
+ struct vbe_controller_info *controller = &vbe_buf.controller;
+ userptr_t video_mode_ptr;
+ uint16_t mode_number;
+ uint16_t status;
+ size_t len;
+ int rc;
+
+ /* Avoid returning uninitialised data on error */
+ *mode_numbers = NULL;
+
+ /* Get controller information block */
+ controller->vbe_signature = 0;
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
+ : "=a" ( status )
+ : "a" ( VBE_CONTROLLER_INFO ),
+ "D" ( __from_data16 ( controller ) )
+ : "memory" );
+ if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
+ DBGC ( &vbe_buf, "VESAFB could not get controller information: "
+ "%s\n", strerror ( rc ) );
+ return rc;
+ }
+ if ( controller->vbe_signature != VBE_CONTROLLER_SIGNATURE ) {
+ DBGC ( &vbe_buf, "VESAFB invalid controller signature "
+ "\"%c%c%c%c\"\n", ( controller->vbe_signature >> 0 ),
+ ( controller->vbe_signature >> 8 ),
+ ( controller->vbe_signature >> 16 ),
+ ( controller->vbe_signature >> 24 ) );
+ DBGC_HDA ( &vbe_buf, 0, controller, sizeof ( *controller ) );
+ return -EINVAL;
+ }
+ DBGC ( &vbe_buf, "VESAFB found VBE version %d.%d with mode list at "
+ "%04x:%04x\n", controller->vbe_major_version,
+ controller->vbe_minor_version,
+ controller->video_mode_ptr.segment,
+ controller->video_mode_ptr.offset );
+
+ /* Calculate length of mode list */
+ video_mode_ptr = real_to_user ( controller->video_mode_ptr.segment,
+ controller->video_mode_ptr.offset );
+ len = 0;
+ do {
+ copy_from_user ( &mode_number, video_mode_ptr, len,
+ sizeof ( mode_number ) );
+ len += sizeof ( mode_number );
+ } while ( mode_number != VBE_MODE_END );
+
+ /* Allocate and fill mode list */
+ *mode_numbers = malloc ( len );
+ if ( ! *mode_numbers )
+ return -ENOMEM;
+ copy_from_user ( *mode_numbers, video_mode_ptr, 0, len );
+
+ return 0;
+}
+
+/**
+ * Set video mode
+ *
+ * @v mode_number Mode number
+ * @v mode Mode information
+ * @v pixbuf Background picture (if any)
+ * @ret rc Return status code
+ */
+static int vesafb_set_mode ( unsigned int mode_number,
+ struct vbe_mode_info *mode,
+ struct pixel_buffer *pixbuf ) {
+ uint16_t status;
+ int rc;
+
+ /* Select this mode */
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
+ : "=a" ( status )
+ : "a" ( VBE_SET_MODE ),
+ "b" ( mode_number ) );
+ if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
+ DBGC ( &vbe_buf, "VESAFB could not set mode %04x: %s\n",
+ mode_number, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Record mode parameters */
+ vesafb.pixel.width = mode->x_resolution;
+ vesafb.pixel.height = mode->y_resolution;
+ vesafb.pixel.len = ( ( mode->bits_per_pixel + 7 ) / 8 );
+ vesafb.pixel.stride = mode->bytes_per_scan_line;
+ DBGC ( &vbe_buf, "VESAFB mode %04x has frame buffer at %08x\n",
+ mode_number, mode->phys_base_ptr );
+
+ /* Initialise font colours */
+ vesafb.map.red_scale = ( 8 - mode->red_mask_size );
+ vesafb.map.green_scale = ( 8 - mode->green_mask_size );
+ vesafb.map.blue_scale = ( 8 - mode->blue_mask_size );
+ vesafb.map.red_lsb = mode->red_field_position;
+ vesafb.map.green_lsb = mode->green_field_position;
+ vesafb.map.blue_lsb = mode->blue_field_position;
+
+ /* Get font data */
+ vesafb_font();
+
+ /* Initialise frame buffer console */
+ fbcon_init ( &vesafb.fbcon, phys_to_user ( mode->phys_base_ptr ),
+ &vesafb.pixel, &vesafb.map, &vesafb.font, pixbuf );
+
+ return 0;
+}
+
+/**
+ * Select and set video mode
+ *
+ * @v min_width Minimum required width (in pixels)
+ * @v min_height Minimum required height (in pixels)
+ * @v min_bpp Minimum required colour depth (in bits per pixel)
+ * @v pixbuf Background picture (if any)
+ * @ret rc Return status code
+ */
+static int vesafb_select_mode ( unsigned int min_width, unsigned int min_height,
+ unsigned int min_bpp,
+ struct pixel_buffer *pixbuf ) {
+ struct vbe_mode_info *mode = &vbe_buf.mode;
+ uint16_t *mode_numbers;
+ uint16_t mode_number;
+ uint16_t status;
+ int rc;
+
+ /* Get VESA mode list */
+ if ( ( rc = vesafb_mode_list ( &mode_numbers ) ) != 0 )
+ goto err_mode_list;
+
+ /* Find the first suitable mode */
+ while ( ( mode_number = *(mode_numbers++) ) != VBE_MODE_END ) {
+
+ /* Force linear mode variant */
+ mode_number |= VBE_MODE_LINEAR;
+
+ /* Get mode information */
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
+ : "=a" ( status )
+ : "a" ( VBE_MODE_INFO ),
+ "c" ( mode_number ),
+ "D" ( __from_data16 ( mode ) )
+ : "memory" );
+ if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
+ DBGC ( &vbe_buf, "VESAFB could not get mode %04x "
+ "information: %s\n", mode_number,
+ strerror ( rc ) );
+ goto err_mode_info;
+ }
+ DBGC ( &vbe_buf, "VESAFB mode %04x %dx%d %dbpp(%d:%d:%d:%d) "
+ "model %02x [x%d]%s%s%s%s%s\n", mode_number,
+ mode->x_resolution, mode->y_resolution,
+ mode->bits_per_pixel, mode->rsvd_mask_size,
+ mode->red_mask_size, mode->green_mask_size,
+ mode->blue_mask_size, mode->memory_model,
+ ( mode->number_of_image_pages + 1 ),
+ ( ( mode->mode_attributes & VBE_MODE_ATTR_SUPPORTED ) ?
+ "" : " [unsupported]" ),
+ ( ( mode->mode_attributes & VBE_MODE_ATTR_TTY ) ?
+ " [tty]" : "" ),
+ ( ( mode->mode_attributes & VBE_MODE_ATTR_GRAPHICS ) ?
+ "" : " [text]" ),
+ ( ( mode->mode_attributes & VBE_MODE_ATTR_LINEAR ) ?
+ "" : " [nonlinear]" ),
+ ( ( mode->mode_attributes & VBE_MODE_ATTR_TRIPLE_BUF ) ?
+ " [buf]" : "" ) );
+
+ /* Skip unusable modes */
+ if ( ( mode->mode_attributes & ( VBE_MODE_ATTR_SUPPORTED |
+ VBE_MODE_ATTR_GRAPHICS |
+ VBE_MODE_ATTR_LINEAR ) ) !=
+ ( VBE_MODE_ATTR_SUPPORTED | VBE_MODE_ATTR_GRAPHICS |
+ VBE_MODE_ATTR_LINEAR ) ) {
+ continue;
+ }
+ if ( mode->memory_model != VBE_MODE_MODEL_DIRECT_COLOUR )
+ continue;
+
+ /* Skip modes not meeting the requirements */
+ if ( ( mode->x_resolution < min_width ) ||
+ ( mode->y_resolution < min_height ) ||
+ ( mode->bits_per_pixel < min_bpp ) ) {
+ continue;
+ }
+
+ /* Select this mode */
+ if ( ( rc = vesafb_set_mode ( mode_number, mode,
+ pixbuf ) ) != 0 ) {
+ goto err_set_mode;
+ }
+
+ break;
+ }
+
+ err_set_mode:
+ err_mode_info:
+ free ( mode_numbers );
+ err_mode_list:
+ return rc;
+}
+
+/**
+ * Print a character to current cursor position
+ *
+ * @v character Character
+ */
+static void vesafb_putchar ( int character ) {
+
+ fbcon_putchar ( &vesafb.fbcon, character );
+}
+
+/**
+ * Configure console
+ *
+ * @v config Console configuration, or NULL to reset
+ * @ret rc Return status code
+ */
+static int vesafb_configure ( struct console_configuration *config ) {
+ uint32_t discard_a;
+ uint32_t discard_b;
+ int rc;
+
+ /* Reset console, if applicable */
+ if ( ! vesafb_console.disabled ) {
+ fbcon_fini ( &vesafb.fbcon );
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
+ : "=a" ( discard_a )
+ : "a" ( VBE_SET_VGA_MODE |
+ vesafb.saved_mode ) );
+ DBGC ( &vbe_buf, "VESAFB restored VGA mode %#02x\n",
+ vesafb.saved_mode );
+ bios_console.disabled &= ~CONSOLE_DISABLED_OUTPUT;
+ }
+ vesafb_console.disabled = CONSOLE_DISABLED;
+
+ /* Record current video mode */
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
+ : "=a" ( vesafb.saved_mode ), "=b" ( discard_b )
+ : "a" ( VBE_GET_VGA_MODE ) );
+
+ /* Do nothing more unless we have a usable configuration */
+ if ( ( config == NULL ) ||
+ ( config->width == 0 ) || ( config->height == 0 ) ) {
+ return 0;
+ }
+
+ /* Try to select an appropriate mode */
+ if ( ( rc = vesafb_select_mode ( config->width, config->height,
+ config->bpp, config->pixbuf ) ) != 0 )
+ return rc;
+
+ /* Mark console as enabled */
+ vesafb_console.disabled = 0;
+ bios_console.disabled |= CONSOLE_DISABLED_OUTPUT;
+
+ return 0;
+}
+
+/** VESA frame buffer console driver */
+struct console_driver vesafb_console __console_driver = {
+ .usage = CONSOLE_VESAFB,
+ .putchar = vesafb_putchar,
+ .configure = vesafb_configure,
+ .disabled = CONSOLE_DISABLED,
+};