|  | @@ -0,0 +1,420 @@
 | 
		
	
		
			
			|  | 1 | +/*
 | 
		
	
		
			
			|  | 2 | + * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
 | 
		
	
		
			
			|  | 3 | + *
 | 
		
	
		
			
			|  | 4 | + * This program is free software; you can redistribute it and/or
 | 
		
	
		
			
			|  | 5 | + * modify it under the terms of the GNU General Public License as
 | 
		
	
		
			
			|  | 6 | + * published by the Free Software Foundation; either version 2 of the
 | 
		
	
		
			
			|  | 7 | + * License, or any later version.
 | 
		
	
		
			
			|  | 8 | + *
 | 
		
	
		
			
			|  | 9 | + * This program is distributed in the hope that it will be useful, but
 | 
		
	
		
			
			|  | 10 | + * WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
		
	
		
			
			|  | 11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
		
	
		
			
			|  | 12 | + * General Public License for more details.
 | 
		
	
		
			
			|  | 13 | + *
 | 
		
	
		
			
			|  | 14 | + * You should have received a copy of the GNU General Public License
 | 
		
	
		
			
			|  | 15 | + * along with this program; if not, write to the Free Software
 | 
		
	
		
			
			|  | 16 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
		
	
		
			
			|  | 17 | + * 02110-1301, USA.
 | 
		
	
		
			
			|  | 18 | + */
 | 
		
	
		
			
			|  | 19 | +
 | 
		
	
		
			
			|  | 20 | +FILE_LICENCE ( GPL2_OR_LATER );
 | 
		
	
		
			
			|  | 21 | +
 | 
		
	
		
			
			|  | 22 | +/** @file
 | 
		
	
		
			
			|  | 23 | + *
 | 
		
	
		
			
			|  | 24 | + * VESA frame buffer console
 | 
		
	
		
			
			|  | 25 | + *
 | 
		
	
		
			
			|  | 26 | + */
 | 
		
	
		
			
			|  | 27 | +
 | 
		
	
		
			
			|  | 28 | +#include <stdlib.h>
 | 
		
	
		
			
			|  | 29 | +#include <errno.h>
 | 
		
	
		
			
			|  | 30 | +#include <realmode.h>
 | 
		
	
		
			
			|  | 31 | +#include <ipxe/console.h>
 | 
		
	
		
			
			|  | 32 | +#include <ipxe/io.h>
 | 
		
	
		
			
			|  | 33 | +#include <ipxe/fbcon.h>
 | 
		
	
		
			
			|  | 34 | +#include <ipxe/vesafb.h>
 | 
		
	
		
			
			|  | 35 | +#include <config/console.h>
 | 
		
	
		
			
			|  | 36 | +
 | 
		
	
		
			
			|  | 37 | +/* Avoid dragging in BIOS console if not otherwise used */
 | 
		
	
		
			
			|  | 38 | +extern struct console_driver bios_console;
 | 
		
	
		
			
			|  | 39 | +struct console_driver bios_console __attribute__ (( weak ));
 | 
		
	
		
			
			|  | 40 | +
 | 
		
	
		
			
			|  | 41 | +/* Disambiguate the various error causes */
 | 
		
	
		
			
			|  | 42 | +#define EIO_FAILED __einfo_error ( EINFO_EIO_FAILED )
 | 
		
	
		
			
			|  | 43 | +#define EINFO_EIO_FAILED						\
 | 
		
	
		
			
			|  | 44 | +	__einfo_uniqify ( EINFO_EIO, 0x01,				\
 | 
		
	
		
			
			|  | 45 | +			  "Function call failed" )
 | 
		
	
		
			
			|  | 46 | +#define EIO_HARDWARE __einfo_error ( EINFO_EIO_HARDWARE )
 | 
		
	
		
			
			|  | 47 | +#define EINFO_EIO_HARDWARE						\
 | 
		
	
		
			
			|  | 48 | +	__einfo_uniqify ( EINFO_EIO, 0x02,				\
 | 
		
	
		
			
			|  | 49 | +			  "Not supported in current configuration" )
 | 
		
	
		
			
			|  | 50 | +#define EIO_MODE __einfo_error ( EINFO_EIO_MODE )
 | 
		
	
		
			
			|  | 51 | +#define EINFO_EIO_MODE							\
 | 
		
	
		
			
			|  | 52 | +	__einfo_uniqify ( EINFO_EIO, 0x03,				\
 | 
		
	
		
			
			|  | 53 | +			  "Invalid in current video mode" )
 | 
		
	
		
			
			|  | 54 | +#define EIO_VBE( code )							\
 | 
		
	
		
			
			|  | 55 | +	EUNIQ ( EINFO_EIO, (code), EIO_FAILED, EIO_HARDWARE, EIO_MODE )
 | 
		
	
		
			
			|  | 56 | +
 | 
		
	
		
			
			|  | 57 | +/* Set default console usage if applicable */
 | 
		
	
		
			
			|  | 58 | +#if ! ( defined ( CONSOLE_VESAFB ) && CONSOLE_EXPLICIT ( CONSOLE_VESAFB ) )
 | 
		
	
		
			
			|  | 59 | +#undef CONSOLE_VESAFB
 | 
		
	
		
			
			|  | 60 | +#define CONSOLE_VESAFB ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
 | 
		
	
		
			
			|  | 61 | +#endif
 | 
		
	
		
			
			|  | 62 | +
 | 
		
	
		
			
			|  | 63 | +/** Font corresponding to selected character width and height */
 | 
		
	
		
			
			|  | 64 | +#define VESAFB_FONT VBE_FONT_8x16
 | 
		
	
		
			
			|  | 65 | +
 | 
		
	
		
			
			|  | 66 | +/* Forward declaration */
 | 
		
	
		
			
			|  | 67 | +struct console_driver vesafb_console __console_driver;
 | 
		
	
		
			
			|  | 68 | +
 | 
		
	
		
			
			|  | 69 | +/** A VESA frame buffer */
 | 
		
	
		
			
			|  | 70 | +struct vesafb {
 | 
		
	
		
			
			|  | 71 | +	/** Frame buffer console */
 | 
		
	
		
			
			|  | 72 | +	struct fbcon fbcon;
 | 
		
	
		
			
			|  | 73 | +	/** Pixel geometry */
 | 
		
	
		
			
			|  | 74 | +	struct fbcon_geometry pixel;
 | 
		
	
		
			
			|  | 75 | +	/** Colour mapping */
 | 
		
	
		
			
			|  | 76 | +	struct fbcon_colour_map map;
 | 
		
	
		
			
			|  | 77 | +	/** Font definition */
 | 
		
	
		
			
			|  | 78 | +	struct fbcon_font font;
 | 
		
	
		
			
			|  | 79 | +	/** Total length */
 | 
		
	
		
			
			|  | 80 | +	size_t len;
 | 
		
	
		
			
			|  | 81 | +	/** Saved VGA mode */
 | 
		
	
		
			
			|  | 82 | +	uint8_t saved_mode;
 | 
		
	
		
			
			|  | 83 | +};
 | 
		
	
		
			
			|  | 84 | +
 | 
		
	
		
			
			|  | 85 | +/** The VESA frame buffer */
 | 
		
	
		
			
			|  | 86 | +static struct vesafb vesafb;
 | 
		
	
		
			
			|  | 87 | +
 | 
		
	
		
			
			|  | 88 | +/** Base memory buffer used for VBE calls */
 | 
		
	
		
			
			|  | 89 | +union vbe_buffer {
 | 
		
	
		
			
			|  | 90 | +	/** VBE controller information block */
 | 
		
	
		
			
			|  | 91 | +	struct vbe_controller_info controller;
 | 
		
	
		
			
			|  | 92 | +	/** VBE mode information block */
 | 
		
	
		
			
			|  | 93 | +	struct vbe_mode_info mode;
 | 
		
	
		
			
			|  | 94 | +};
 | 
		
	
		
			
			|  | 95 | +static union vbe_buffer __bss16 ( vbe_buf );
 | 
		
	
		
			
			|  | 96 | +#define vbe_buf __use_data16 ( vbe_buf )
 | 
		
	
		
			
			|  | 97 | +
 | 
		
	
		
			
			|  | 98 | +/**
 | 
		
	
		
			
			|  | 99 | + * Convert VBE status code to iPXE status code
 | 
		
	
		
			
			|  | 100 | + *
 | 
		
	
		
			
			|  | 101 | + * @v status		VBE status code
 | 
		
	
		
			
			|  | 102 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 103 | + */
 | 
		
	
		
			
			|  | 104 | +static int vesafb_rc ( unsigned int status ) {
 | 
		
	
		
			
			|  | 105 | +	unsigned int code;
 | 
		
	
		
			
			|  | 106 | +
 | 
		
	
		
			
			|  | 107 | +	if ( ( status & 0xff ) != 0x4f )
 | 
		
	
		
			
			|  | 108 | +		return -ENOTSUP;
 | 
		
	
		
			
			|  | 109 | +	code = ( ( status >> 8 ) & 0xff );
 | 
		
	
		
			
			|  | 110 | +	return ( code ? -EIO_VBE ( code ) : 0 );
 | 
		
	
		
			
			|  | 111 | +}
 | 
		
	
		
			
			|  | 112 | +
 | 
		
	
		
			
			|  | 113 | +/**
 | 
		
	
		
			
			|  | 114 | + * Get font definition
 | 
		
	
		
			
			|  | 115 | + *
 | 
		
	
		
			
			|  | 116 | + */
 | 
		
	
		
			
			|  | 117 | +static void vesafb_font ( void ) {
 | 
		
	
		
			
			|  | 118 | +	struct segoff font;
 | 
		
	
		
			
			|  | 119 | +
 | 
		
	
		
			
			|  | 120 | +	/* Get font information
 | 
		
	
		
			
			|  | 121 | +	 *
 | 
		
	
		
			
			|  | 122 | +	 * Working around gcc bugs is icky here.  The value we want is
 | 
		
	
		
			
			|  | 123 | +	 * returned in %ebp, but there's no way to specify %ebp in an
 | 
		
	
		
			
			|  | 124 | +	 * output constraint.  We can't put %ebp in the clobber list,
 | 
		
	
		
			
			|  | 125 | +	 * because this tends to cause random build failures on some
 | 
		
	
		
			
			|  | 126 | +	 * gcc versions.  We can't manually push/pop %ebp and return
 | 
		
	
		
			
			|  | 127 | +	 * the value via a generic register output constraint, because
 | 
		
	
		
			
			|  | 128 | +	 * gcc might choose to use %ebp to satisfy that constraint
 | 
		
	
		
			
			|  | 129 | +	 * (and we have no way to prevent it from so doing).
 | 
		
	
		
			
			|  | 130 | +	 *
 | 
		
	
		
			
			|  | 131 | +	 * Work around this hideous mess by using %ecx and %edx as the
 | 
		
	
		
			
			|  | 132 | +	 * output registers, since they get clobbered anyway.
 | 
		
	
		
			
			|  | 133 | +	 */
 | 
		
	
		
			
			|  | 134 | +	__asm__ __volatile__ ( REAL_CODE ( "pushw %%bp\n\t" /* gcc bug */
 | 
		
	
		
			
			|  | 135 | +					   "int $0x10\n\t"
 | 
		
	
		
			
			|  | 136 | +					   "movw %%es, %%cx\n\t"
 | 
		
	
		
			
			|  | 137 | +					   "movw %%bp, %%dx\n\t"
 | 
		
	
		
			
			|  | 138 | +					   "popw %%bp\n\t" /* gcc bug */ )
 | 
		
	
		
			
			|  | 139 | +			       : "=c" ( font.segment ),
 | 
		
	
		
			
			|  | 140 | +				 "=d" ( font.offset )
 | 
		
	
		
			
			|  | 141 | +			       : "a" ( VBE_GET_FONT ),
 | 
		
	
		
			
			|  | 142 | +				 "b" ( VESAFB_FONT ) );
 | 
		
	
		
			
			|  | 143 | +	DBGC ( &vbe_buf, "VESAFB has font %04x at %04x:%04x\n",
 | 
		
	
		
			
			|  | 144 | +	       VESAFB_FONT, font.segment, font.offset );
 | 
		
	
		
			
			|  | 145 | +	vesafb.font.start = real_to_user ( font.segment, font.offset );
 | 
		
	
		
			
			|  | 146 | +}
 | 
		
	
		
			
			|  | 147 | +
 | 
		
	
		
			
			|  | 148 | +/**
 | 
		
	
		
			
			|  | 149 | + * Get VBE mode list
 | 
		
	
		
			
			|  | 150 | + *
 | 
		
	
		
			
			|  | 151 | + * @ret mode_numbers	Mode number list (terminated with VBE_MODE_END)
 | 
		
	
		
			
			|  | 152 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 153 | + *
 | 
		
	
		
			
			|  | 154 | + * The caller is responsible for eventually freeing the mode list.
 | 
		
	
		
			
			|  | 155 | + */
 | 
		
	
		
			
			|  | 156 | +static int vesafb_mode_list ( uint16_t **mode_numbers ) {
 | 
		
	
		
			
			|  | 157 | +	struct vbe_controller_info *controller = &vbe_buf.controller;
 | 
		
	
		
			
			|  | 158 | +	userptr_t video_mode_ptr;
 | 
		
	
		
			
			|  | 159 | +	uint16_t mode_number;
 | 
		
	
		
			
			|  | 160 | +	uint16_t status;
 | 
		
	
		
			
			|  | 161 | +	size_t len;
 | 
		
	
		
			
			|  | 162 | +	int rc;
 | 
		
	
		
			
			|  | 163 | +
 | 
		
	
		
			
			|  | 164 | +	/* Avoid returning uninitialised data on error */
 | 
		
	
		
			
			|  | 165 | +	*mode_numbers = NULL;
 | 
		
	
		
			
			|  | 166 | +
 | 
		
	
		
			
			|  | 167 | +	/* Get controller information block */
 | 
		
	
		
			
			|  | 168 | +	controller->vbe_signature = 0;
 | 
		
	
		
			
			|  | 169 | +	__asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
 | 
		
	
		
			
			|  | 170 | +			       : "=a" ( status )
 | 
		
	
		
			
			|  | 171 | +			       : "a" ( VBE_CONTROLLER_INFO ),
 | 
		
	
		
			
			|  | 172 | +				 "D" ( __from_data16 ( controller ) )
 | 
		
	
		
			
			|  | 173 | +			       : "memory" );
 | 
		
	
		
			
			|  | 174 | +	if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
 | 
		
	
		
			
			|  | 175 | +		DBGC ( &vbe_buf, "VESAFB could not get controller information: "
 | 
		
	
		
			
			|  | 176 | +		       "%s\n", strerror ( rc ) );
 | 
		
	
		
			
			|  | 177 | +		return rc;
 | 
		
	
		
			
			|  | 178 | +	}
 | 
		
	
		
			
			|  | 179 | +	if ( controller->vbe_signature != VBE_CONTROLLER_SIGNATURE ) {
 | 
		
	
		
			
			|  | 180 | +		DBGC ( &vbe_buf, "VESAFB invalid controller signature "
 | 
		
	
		
			
			|  | 181 | +		       "\"%c%c%c%c\"\n", ( controller->vbe_signature >> 0 ),
 | 
		
	
		
			
			|  | 182 | +		       ( controller->vbe_signature >> 8 ),
 | 
		
	
		
			
			|  | 183 | +		       ( controller->vbe_signature >> 16 ),
 | 
		
	
		
			
			|  | 184 | +		       ( controller->vbe_signature >> 24 ) );
 | 
		
	
		
			
			|  | 185 | +		DBGC_HDA ( &vbe_buf, 0, controller, sizeof ( *controller ) );
 | 
		
	
		
			
			|  | 186 | +		return -EINVAL;
 | 
		
	
		
			
			|  | 187 | +	}
 | 
		
	
		
			
			|  | 188 | +	DBGC ( &vbe_buf, "VESAFB found VBE version %d.%d with mode list at "
 | 
		
	
		
			
			|  | 189 | +	       "%04x:%04x\n", controller->vbe_major_version,
 | 
		
	
		
			
			|  | 190 | +	       controller->vbe_minor_version,
 | 
		
	
		
			
			|  | 191 | +	       controller->video_mode_ptr.segment,
 | 
		
	
		
			
			|  | 192 | +	       controller->video_mode_ptr.offset );
 | 
		
	
		
			
			|  | 193 | +
 | 
		
	
		
			
			|  | 194 | +	/* Calculate length of mode list */
 | 
		
	
		
			
			|  | 195 | +	video_mode_ptr = real_to_user ( controller->video_mode_ptr.segment,
 | 
		
	
		
			
			|  | 196 | +					controller->video_mode_ptr.offset );
 | 
		
	
		
			
			|  | 197 | +	len = 0;
 | 
		
	
		
			
			|  | 198 | +	do {
 | 
		
	
		
			
			|  | 199 | +		copy_from_user ( &mode_number, video_mode_ptr, len,
 | 
		
	
		
			
			|  | 200 | +				 sizeof ( mode_number ) );
 | 
		
	
		
			
			|  | 201 | +		len += sizeof ( mode_number );
 | 
		
	
		
			
			|  | 202 | +	} while ( mode_number != VBE_MODE_END );
 | 
		
	
		
			
			|  | 203 | +
 | 
		
	
		
			
			|  | 204 | +	/* Allocate and fill mode list */
 | 
		
	
		
			
			|  | 205 | +	*mode_numbers = malloc ( len );
 | 
		
	
		
			
			|  | 206 | +	if ( ! *mode_numbers )
 | 
		
	
		
			
			|  | 207 | +		return -ENOMEM;
 | 
		
	
		
			
			|  | 208 | +	copy_from_user ( *mode_numbers, video_mode_ptr, 0, len );
 | 
		
	
		
			
			|  | 209 | +
 | 
		
	
		
			
			|  | 210 | +	return 0;
 | 
		
	
		
			
			|  | 211 | +}
 | 
		
	
		
			
			|  | 212 | +
 | 
		
	
		
			
			|  | 213 | +/**
 | 
		
	
		
			
			|  | 214 | + * Set video mode
 | 
		
	
		
			
			|  | 215 | + *
 | 
		
	
		
			
			|  | 216 | + * @v mode_number	Mode number
 | 
		
	
		
			
			|  | 217 | + * @v mode		Mode information
 | 
		
	
		
			
			|  | 218 | + * @v pixbuf		Background picture (if any)
 | 
		
	
		
			
			|  | 219 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 220 | + */
 | 
		
	
		
			
			|  | 221 | +static int vesafb_set_mode ( unsigned int mode_number,
 | 
		
	
		
			
			|  | 222 | +			     struct vbe_mode_info *mode,
 | 
		
	
		
			
			|  | 223 | +			     struct pixel_buffer *pixbuf ) {
 | 
		
	
		
			
			|  | 224 | +	uint16_t status;
 | 
		
	
		
			
			|  | 225 | +	int rc;
 | 
		
	
		
			
			|  | 226 | +
 | 
		
	
		
			
			|  | 227 | +	/* Select this mode */
 | 
		
	
		
			
			|  | 228 | +	__asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
 | 
		
	
		
			
			|  | 229 | +			       : "=a" ( status )
 | 
		
	
		
			
			|  | 230 | +			       : "a" ( VBE_SET_MODE ),
 | 
		
	
		
			
			|  | 231 | +				 "b" ( mode_number ) );
 | 
		
	
		
			
			|  | 232 | +	if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
 | 
		
	
		
			
			|  | 233 | +		DBGC ( &vbe_buf, "VESAFB could not set mode %04x: %s\n",
 | 
		
	
		
			
			|  | 234 | +		       mode_number, strerror ( rc ) );
 | 
		
	
		
			
			|  | 235 | +		return rc;
 | 
		
	
		
			
			|  | 236 | +	}
 | 
		
	
		
			
			|  | 237 | +
 | 
		
	
		
			
			|  | 238 | +	/* Record mode parameters */
 | 
		
	
		
			
			|  | 239 | +	vesafb.pixel.width = mode->x_resolution;
 | 
		
	
		
			
			|  | 240 | +	vesafb.pixel.height = mode->y_resolution;
 | 
		
	
		
			
			|  | 241 | +	vesafb.pixel.len = ( ( mode->bits_per_pixel + 7 ) / 8 );
 | 
		
	
		
			
			|  | 242 | +	vesafb.pixel.stride = mode->bytes_per_scan_line;
 | 
		
	
		
			
			|  | 243 | +	DBGC ( &vbe_buf, "VESAFB mode %04x has frame buffer at %08x\n",
 | 
		
	
		
			
			|  | 244 | +	       mode_number, mode->phys_base_ptr );
 | 
		
	
		
			
			|  | 245 | +
 | 
		
	
		
			
			|  | 246 | +	/* Initialise font colours */
 | 
		
	
		
			
			|  | 247 | +	vesafb.map.red_scale = ( 8 - mode->red_mask_size );
 | 
		
	
		
			
			|  | 248 | +	vesafb.map.green_scale = ( 8 - mode->green_mask_size );
 | 
		
	
		
			
			|  | 249 | +	vesafb.map.blue_scale = ( 8 - mode->blue_mask_size );
 | 
		
	
		
			
			|  | 250 | +	vesafb.map.red_lsb = mode->red_field_position;
 | 
		
	
		
			
			|  | 251 | +	vesafb.map.green_lsb = mode->green_field_position;
 | 
		
	
		
			
			|  | 252 | +	vesafb.map.blue_lsb = mode->blue_field_position;
 | 
		
	
		
			
			|  | 253 | +
 | 
		
	
		
			
			|  | 254 | +	/* Get font data */
 | 
		
	
		
			
			|  | 255 | +	vesafb_font();
 | 
		
	
		
			
			|  | 256 | +
 | 
		
	
		
			
			|  | 257 | +	/* Initialise frame buffer console */
 | 
		
	
		
			
			|  | 258 | +	fbcon_init ( &vesafb.fbcon, phys_to_user ( mode->phys_base_ptr ),
 | 
		
	
		
			
			|  | 259 | +		     &vesafb.pixel, &vesafb.map, &vesafb.font, pixbuf );
 | 
		
	
		
			
			|  | 260 | +
 | 
		
	
		
			
			|  | 261 | +	return 0;
 | 
		
	
		
			
			|  | 262 | +}
 | 
		
	
		
			
			|  | 263 | +
 | 
		
	
		
			
			|  | 264 | +/**
 | 
		
	
		
			
			|  | 265 | + * Select and set video mode
 | 
		
	
		
			
			|  | 266 | + *
 | 
		
	
		
			
			|  | 267 | + * @v min_width		Minimum required width (in pixels)
 | 
		
	
		
			
			|  | 268 | + * @v min_height	Minimum required height (in pixels)
 | 
		
	
		
			
			|  | 269 | + * @v min_bpp		Minimum required colour depth (in bits per pixel)
 | 
		
	
		
			
			|  | 270 | + * @v pixbuf		Background picture (if any)
 | 
		
	
		
			
			|  | 271 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 272 | + */
 | 
		
	
		
			
			|  | 273 | +static int vesafb_select_mode ( unsigned int min_width, unsigned int min_height,
 | 
		
	
		
			
			|  | 274 | +				unsigned int min_bpp,
 | 
		
	
		
			
			|  | 275 | +				struct pixel_buffer *pixbuf ) {
 | 
		
	
		
			
			|  | 276 | +	struct vbe_mode_info *mode = &vbe_buf.mode;
 | 
		
	
		
			
			|  | 277 | +	uint16_t *mode_numbers;
 | 
		
	
		
			
			|  | 278 | +	uint16_t mode_number;
 | 
		
	
		
			
			|  | 279 | +	uint16_t status;
 | 
		
	
		
			
			|  | 280 | +	int rc;
 | 
		
	
		
			
			|  | 281 | +
 | 
		
	
		
			
			|  | 282 | +	/* Get VESA mode list */
 | 
		
	
		
			
			|  | 283 | +	if ( ( rc = vesafb_mode_list ( &mode_numbers ) ) != 0 )
 | 
		
	
		
			
			|  | 284 | +		goto err_mode_list;
 | 
		
	
		
			
			|  | 285 | +
 | 
		
	
		
			
			|  | 286 | +	/* Find the first suitable mode */
 | 
		
	
		
			
			|  | 287 | +	while ( ( mode_number = *(mode_numbers++) ) != VBE_MODE_END ) {
 | 
		
	
		
			
			|  | 288 | +
 | 
		
	
		
			
			|  | 289 | +		/* Force linear mode variant */
 | 
		
	
		
			
			|  | 290 | +		mode_number |= VBE_MODE_LINEAR;
 | 
		
	
		
			
			|  | 291 | +
 | 
		
	
		
			
			|  | 292 | +		/* Get mode information */
 | 
		
	
		
			
			|  | 293 | +		__asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
 | 
		
	
		
			
			|  | 294 | +				       : "=a" ( status )
 | 
		
	
		
			
			|  | 295 | +				       : "a" ( VBE_MODE_INFO ),
 | 
		
	
		
			
			|  | 296 | +					 "c" ( mode_number ),
 | 
		
	
		
			
			|  | 297 | +					 "D" ( __from_data16 ( mode ) )
 | 
		
	
		
			
			|  | 298 | +				       : "memory" );
 | 
		
	
		
			
			|  | 299 | +		if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
 | 
		
	
		
			
			|  | 300 | +			DBGC ( &vbe_buf, "VESAFB could not get mode %04x "
 | 
		
	
		
			
			|  | 301 | +			       "information: %s\n", mode_number,
 | 
		
	
		
			
			|  | 302 | +			       strerror ( rc ) );
 | 
		
	
		
			
			|  | 303 | +			goto err_mode_info;
 | 
		
	
		
			
			|  | 304 | +		}
 | 
		
	
		
			
			|  | 305 | +		DBGC ( &vbe_buf, "VESAFB mode %04x %dx%d %dbpp(%d:%d:%d:%d) "
 | 
		
	
		
			
			|  | 306 | +		       "model %02x [x%d]%s%s%s%s%s\n", mode_number,
 | 
		
	
		
			
			|  | 307 | +		       mode->x_resolution, mode->y_resolution,
 | 
		
	
		
			
			|  | 308 | +		       mode->bits_per_pixel, mode->rsvd_mask_size,
 | 
		
	
		
			
			|  | 309 | +		       mode->red_mask_size,  mode->green_mask_size,
 | 
		
	
		
			
			|  | 310 | +		       mode->blue_mask_size, mode->memory_model,
 | 
		
	
		
			
			|  | 311 | +		       ( mode->number_of_image_pages + 1 ),
 | 
		
	
		
			
			|  | 312 | +		       ( ( mode->mode_attributes & VBE_MODE_ATTR_SUPPORTED ) ?
 | 
		
	
		
			
			|  | 313 | +			 "" : " [unsupported]" ),
 | 
		
	
		
			
			|  | 314 | +		       ( ( mode->mode_attributes & VBE_MODE_ATTR_TTY ) ?
 | 
		
	
		
			
			|  | 315 | +			 " [tty]" : "" ),
 | 
		
	
		
			
			|  | 316 | +		       ( ( mode->mode_attributes & VBE_MODE_ATTR_GRAPHICS ) ?
 | 
		
	
		
			
			|  | 317 | +			 "" : " [text]" ),
 | 
		
	
		
			
			|  | 318 | +		       ( ( mode->mode_attributes & VBE_MODE_ATTR_LINEAR ) ?
 | 
		
	
		
			
			|  | 319 | +			 "" : " [nonlinear]" ),
 | 
		
	
		
			
			|  | 320 | +		       ( ( mode->mode_attributes & VBE_MODE_ATTR_TRIPLE_BUF ) ?
 | 
		
	
		
			
			|  | 321 | +			 " [buf]" : "" ) );
 | 
		
	
		
			
			|  | 322 | +
 | 
		
	
		
			
			|  | 323 | +		/* Skip unusable modes */
 | 
		
	
		
			
			|  | 324 | +		if ( ( mode->mode_attributes & ( VBE_MODE_ATTR_SUPPORTED |
 | 
		
	
		
			
			|  | 325 | +						 VBE_MODE_ATTR_GRAPHICS |
 | 
		
	
		
			
			|  | 326 | +						 VBE_MODE_ATTR_LINEAR ) ) !=
 | 
		
	
		
			
			|  | 327 | +		     ( VBE_MODE_ATTR_SUPPORTED | VBE_MODE_ATTR_GRAPHICS |
 | 
		
	
		
			
			|  | 328 | +		       VBE_MODE_ATTR_LINEAR ) ) {
 | 
		
	
		
			
			|  | 329 | +			continue;
 | 
		
	
		
			
			|  | 330 | +		}
 | 
		
	
		
			
			|  | 331 | +		if ( mode->memory_model != VBE_MODE_MODEL_DIRECT_COLOUR )
 | 
		
	
		
			
			|  | 332 | +			continue;
 | 
		
	
		
			
			|  | 333 | +
 | 
		
	
		
			
			|  | 334 | +		/* Skip modes not meeting the requirements */
 | 
		
	
		
			
			|  | 335 | +		if ( ( mode->x_resolution < min_width ) ||
 | 
		
	
		
			
			|  | 336 | +		     ( mode->y_resolution < min_height ) ||
 | 
		
	
		
			
			|  | 337 | +		     ( mode->bits_per_pixel < min_bpp ) ) {
 | 
		
	
		
			
			|  | 338 | +			continue;
 | 
		
	
		
			
			|  | 339 | +		}
 | 
		
	
		
			
			|  | 340 | +
 | 
		
	
		
			
			|  | 341 | +		/* Select this mode */
 | 
		
	
		
			
			|  | 342 | +		if ( ( rc = vesafb_set_mode ( mode_number, mode,
 | 
		
	
		
			
			|  | 343 | +					      pixbuf ) ) != 0 ) {
 | 
		
	
		
			
			|  | 344 | +			goto err_set_mode;
 | 
		
	
		
			
			|  | 345 | +		}
 | 
		
	
		
			
			|  | 346 | +
 | 
		
	
		
			
			|  | 347 | +		break;
 | 
		
	
		
			
			|  | 348 | +	}
 | 
		
	
		
			
			|  | 349 | +
 | 
		
	
		
			
			|  | 350 | + err_set_mode:
 | 
		
	
		
			
			|  | 351 | + err_mode_info:
 | 
		
	
		
			
			|  | 352 | +	free ( mode_numbers );
 | 
		
	
		
			
			|  | 353 | + err_mode_list:
 | 
		
	
		
			
			|  | 354 | +	return rc;
 | 
		
	
		
			
			|  | 355 | +}
 | 
		
	
		
			
			|  | 356 | +
 | 
		
	
		
			
			|  | 357 | +/**
 | 
		
	
		
			
			|  | 358 | + * Print a character to current cursor position
 | 
		
	
		
			
			|  | 359 | + *
 | 
		
	
		
			
			|  | 360 | + * @v character		Character
 | 
		
	
		
			
			|  | 361 | + */
 | 
		
	
		
			
			|  | 362 | +static void vesafb_putchar ( int character ) {
 | 
		
	
		
			
			|  | 363 | +
 | 
		
	
		
			
			|  | 364 | +	fbcon_putchar ( &vesafb.fbcon, character );
 | 
		
	
		
			
			|  | 365 | +}
 | 
		
	
		
			
			|  | 366 | +
 | 
		
	
		
			
			|  | 367 | +/**
 | 
		
	
		
			
			|  | 368 | + * Configure console
 | 
		
	
		
			
			|  | 369 | + *
 | 
		
	
		
			
			|  | 370 | + * @v config		Console configuration, or NULL to reset
 | 
		
	
		
			
			|  | 371 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 372 | + */
 | 
		
	
		
			
			|  | 373 | +static int vesafb_configure ( struct console_configuration *config ) {
 | 
		
	
		
			
			|  | 374 | +	uint32_t discard_a;
 | 
		
	
		
			
			|  | 375 | +	uint32_t discard_b;
 | 
		
	
		
			
			|  | 376 | +	int rc;
 | 
		
	
		
			
			|  | 377 | +
 | 
		
	
		
			
			|  | 378 | +	/* Reset console, if applicable */
 | 
		
	
		
			
			|  | 379 | +	if ( ! vesafb_console.disabled ) {
 | 
		
	
		
			
			|  | 380 | +		fbcon_fini ( &vesafb.fbcon );
 | 
		
	
		
			
			|  | 381 | +		__asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
 | 
		
	
		
			
			|  | 382 | +				       : "=a" ( discard_a )
 | 
		
	
		
			
			|  | 383 | +				       : "a" ( VBE_SET_VGA_MODE |
 | 
		
	
		
			
			|  | 384 | +					       vesafb.saved_mode ) );
 | 
		
	
		
			
			|  | 385 | +		DBGC ( &vbe_buf, "VESAFB restored VGA mode %#02x\n",
 | 
		
	
		
			
			|  | 386 | +		       vesafb.saved_mode );
 | 
		
	
		
			
			|  | 387 | +		bios_console.disabled &= ~CONSOLE_DISABLED_OUTPUT;
 | 
		
	
		
			
			|  | 388 | +	}
 | 
		
	
		
			
			|  | 389 | +	vesafb_console.disabled = CONSOLE_DISABLED;
 | 
		
	
		
			
			|  | 390 | +
 | 
		
	
		
			
			|  | 391 | +	/* Record current video mode */
 | 
		
	
		
			
			|  | 392 | +	__asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
 | 
		
	
		
			
			|  | 393 | +			       : "=a" ( vesafb.saved_mode ), "=b" ( discard_b )
 | 
		
	
		
			
			|  | 394 | +			       : "a" ( VBE_GET_VGA_MODE ) );
 | 
		
	
		
			
			|  | 395 | +
 | 
		
	
		
			
			|  | 396 | +	/* Do nothing more unless we have a usable configuration */
 | 
		
	
		
			
			|  | 397 | +	if ( ( config == NULL ) ||
 | 
		
	
		
			
			|  | 398 | +	     ( config->width == 0 ) || ( config->height == 0 ) ) {
 | 
		
	
		
			
			|  | 399 | +		return 0;
 | 
		
	
		
			
			|  | 400 | +	}
 | 
		
	
		
			
			|  | 401 | +
 | 
		
	
		
			
			|  | 402 | +	/* Try to select an appropriate mode */
 | 
		
	
		
			
			|  | 403 | +	if ( ( rc = vesafb_select_mode ( config->width, config->height,
 | 
		
	
		
			
			|  | 404 | +					 config->bpp, config->pixbuf ) ) != 0 )
 | 
		
	
		
			
			|  | 405 | +		return rc;
 | 
		
	
		
			
			|  | 406 | +
 | 
		
	
		
			
			|  | 407 | +	/* Mark console as enabled */
 | 
		
	
		
			
			|  | 408 | +	vesafb_console.disabled = 0;
 | 
		
	
		
			
			|  | 409 | +	bios_console.disabled |= CONSOLE_DISABLED_OUTPUT;
 | 
		
	
		
			
			|  | 410 | +
 | 
		
	
		
			
			|  | 411 | +	return 0;
 | 
		
	
		
			
			|  | 412 | +}
 | 
		
	
		
			
			|  | 413 | +
 | 
		
	
		
			
			|  | 414 | +/** VESA frame buffer console driver */
 | 
		
	
		
			
			|  | 415 | +struct console_driver vesafb_console __console_driver = {
 | 
		
	
		
			
			|  | 416 | +	.usage = CONSOLE_VESAFB,
 | 
		
	
		
			
			|  | 417 | +	.putchar = vesafb_putchar,
 | 
		
	
		
			
			|  | 418 | +	.configure = vesafb_configure,
 | 
		
	
		
			
			|  | 419 | +	.disabled = CONSOLE_DISABLED,
 | 
		
	
		
			
			|  | 420 | +};
 |