|  | @@ -0,0 +1,551 @@
 | 
		
	
		
			
			|  | 1 | +/*
 | 
		
	
		
			
			|  | 2 | + * Copyright (C) 2015 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 | + * You can also choose to distribute this program under the terms of
 | 
		
	
		
			
			|  | 20 | + * the Unmodified Binary Distribution Licence (as given in the file
 | 
		
	
		
			
			|  | 21 | + * COPYING.UBDL), provided that you have satisfied its requirements.
 | 
		
	
		
			
			|  | 22 | + */
 | 
		
	
		
			
			|  | 23 | +
 | 
		
	
		
			
			|  | 24 | +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 | 
		
	
		
			
			|  | 25 | +
 | 
		
	
		
			
			|  | 26 | +/**
 | 
		
	
		
			
			|  | 27 | + * @file
 | 
		
	
		
			
			|  | 28 | + *
 | 
		
	
		
			
			|  | 29 | + * EFI frame buffer console
 | 
		
	
		
			
			|  | 30 | + *
 | 
		
	
		
			
			|  | 31 | + */
 | 
		
	
		
			
			|  | 32 | +
 | 
		
	
		
			
			|  | 33 | +#include <string.h>
 | 
		
	
		
			
			|  | 34 | +#include <strings.h>
 | 
		
	
		
			
			|  | 35 | +#include <ctype.h>
 | 
		
	
		
			
			|  | 36 | +#include <errno.h>
 | 
		
	
		
			
			|  | 37 | +#include <assert.h>
 | 
		
	
		
			
			|  | 38 | +#include <limits.h>
 | 
		
	
		
			
			|  | 39 | +#include <ipxe/efi/efi.h>
 | 
		
	
		
			
			|  | 40 | +#include <ipxe/efi/Protocol/GraphicsOutput.h>
 | 
		
	
		
			
			|  | 41 | +#include <ipxe/efi/Protocol/HiiFont.h>
 | 
		
	
		
			
			|  | 42 | +#include <ipxe/ansicol.h>
 | 
		
	
		
			
			|  | 43 | +#include <ipxe/fbcon.h>
 | 
		
	
		
			
			|  | 44 | +#include <ipxe/console.h>
 | 
		
	
		
			
			|  | 45 | +#include <ipxe/umalloc.h>
 | 
		
	
		
			
			|  | 46 | +#include <ipxe/rotate.h>
 | 
		
	
		
			
			|  | 47 | +#include <config/console.h>
 | 
		
	
		
			
			|  | 48 | +
 | 
		
	
		
			
			|  | 49 | +/* Avoid dragging in EFI console if not otherwise used */
 | 
		
	
		
			
			|  | 50 | +extern struct console_driver efi_console;
 | 
		
	
		
			
			|  | 51 | +struct console_driver efi_console __attribute__ (( weak ));
 | 
		
	
		
			
			|  | 52 | +
 | 
		
	
		
			
			|  | 53 | +/* Set default console usage if applicable
 | 
		
	
		
			
			|  | 54 | + *
 | 
		
	
		
			
			|  | 55 | + * We accept either CONSOLE_FRAMEBUFFER or CONSOLE_EFIFB.
 | 
		
	
		
			
			|  | 56 | + */
 | 
		
	
		
			
			|  | 57 | +#if ( defined ( CONSOLE_FRAMEBUFFER ) && ! defined ( CONSOLE_EFIFB ) )
 | 
		
	
		
			
			|  | 58 | +#define CONSOLE_EFIFB CONSOLE_FRAMEBUFFER
 | 
		
	
		
			
			|  | 59 | +#endif
 | 
		
	
		
			
			|  | 60 | +#if ! ( defined ( CONSOLE_EFIFB ) && CONSOLE_EXPLICIT ( CONSOLE_EFIFB ) )
 | 
		
	
		
			
			|  | 61 | +#undef CONSOLE_EFIFB
 | 
		
	
		
			
			|  | 62 | +#define CONSOLE_EFIFB ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
 | 
		
	
		
			
			|  | 63 | +#endif
 | 
		
	
		
			
			|  | 64 | +
 | 
		
	
		
			
			|  | 65 | +/* Forward declaration */
 | 
		
	
		
			
			|  | 66 | +struct console_driver efifb_console __console_driver;
 | 
		
	
		
			
			|  | 67 | +
 | 
		
	
		
			
			|  | 68 | +/** An EFI frame buffer */
 | 
		
	
		
			
			|  | 69 | +struct efifb {
 | 
		
	
		
			
			|  | 70 | +	/** EFI graphics output protocol */
 | 
		
	
		
			
			|  | 71 | +	EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
 | 
		
	
		
			
			|  | 72 | +	/** EFI HII font protocol */
 | 
		
	
		
			
			|  | 73 | +	EFI_HII_FONT_PROTOCOL *hiifont;
 | 
		
	
		
			
			|  | 74 | +	/** Saved mode */
 | 
		
	
		
			
			|  | 75 | +	UINT32 saved_mode;
 | 
		
	
		
			
			|  | 76 | +
 | 
		
	
		
			
			|  | 77 | +	/** Frame buffer console */
 | 
		
	
		
			
			|  | 78 | +	struct fbcon fbcon;
 | 
		
	
		
			
			|  | 79 | +	/** Physical start address */
 | 
		
	
		
			
			|  | 80 | +	physaddr_t start;
 | 
		
	
		
			
			|  | 81 | +	/** Pixel geometry */
 | 
		
	
		
			
			|  | 82 | +	struct fbcon_geometry pixel;
 | 
		
	
		
			
			|  | 83 | +	/** Colour mapping */
 | 
		
	
		
			
			|  | 84 | +	struct fbcon_colour_map map;
 | 
		
	
		
			
			|  | 85 | +	/** Font definition */
 | 
		
	
		
			
			|  | 86 | +	struct fbcon_font font;
 | 
		
	
		
			
			|  | 87 | +	/** Character glyphs */
 | 
		
	
		
			
			|  | 88 | +	userptr_t glyphs;
 | 
		
	
		
			
			|  | 89 | +};
 | 
		
	
		
			
			|  | 90 | +
 | 
		
	
		
			
			|  | 91 | +/** The EFI frame buffer */
 | 
		
	
		
			
			|  | 92 | +static struct efifb efifb;
 | 
		
	
		
			
			|  | 93 | +
 | 
		
	
		
			
			|  | 94 | +/**
 | 
		
	
		
			
			|  | 95 | + * Get character glyph
 | 
		
	
		
			
			|  | 96 | + *
 | 
		
	
		
			
			|  | 97 | + * @v character		Character
 | 
		
	
		
			
			|  | 98 | + * @v glyph		Character glyph to fill in
 | 
		
	
		
			
			|  | 99 | + */
 | 
		
	
		
			
			|  | 100 | +static void efifb_glyph ( unsigned int character, uint8_t *glyph ) {
 | 
		
	
		
			
			|  | 101 | +	size_t offset = ( character * efifb.font.height );
 | 
		
	
		
			
			|  | 102 | +
 | 
		
	
		
			
			|  | 103 | +	copy_from_user ( glyph, efifb.glyphs, offset, efifb.font.height );
 | 
		
	
		
			
			|  | 104 | +}
 | 
		
	
		
			
			|  | 105 | +
 | 
		
	
		
			
			|  | 106 | +/**
 | 
		
	
		
			
			|  | 107 | + * Get character glyphs
 | 
		
	
		
			
			|  | 108 | + *
 | 
		
	
		
			
			|  | 109 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 110 | + */
 | 
		
	
		
			
			|  | 111 | +static int efifb_glyphs ( void ) {
 | 
		
	
		
			
			|  | 112 | +	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
 | 
		
	
		
			
			|  | 113 | +	EFI_FONT_DISPLAY_INFO *info;
 | 
		
	
		
			
			|  | 114 | +	EFI_IMAGE_OUTPUT *blt;
 | 
		
	
		
			
			|  | 115 | +	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixel;
 | 
		
	
		
			
			|  | 116 | +	size_t offset;
 | 
		
	
		
			
			|  | 117 | +	size_t len;
 | 
		
	
		
			
			|  | 118 | +	uint8_t bitmask;
 | 
		
	
		
			
			|  | 119 | +	unsigned int character;
 | 
		
	
		
			
			|  | 120 | +	unsigned int x;
 | 
		
	
		
			
			|  | 121 | +	unsigned int y;
 | 
		
	
		
			
			|  | 122 | +	EFI_STATUS efirc;
 | 
		
	
		
			
			|  | 123 | +	int rc;
 | 
		
	
		
			
			|  | 124 | +
 | 
		
	
		
			
			|  | 125 | +	/* Get font height */
 | 
		
	
		
			
			|  | 126 | +	if ( ( efirc = efifb.hiifont->GetFontInfo ( efifb.hiifont, NULL, NULL,
 | 
		
	
		
			
			|  | 127 | +						    &info, NULL ) ) != 0 ) {
 | 
		
	
		
			
			|  | 128 | +		rc = -EEFI ( efirc );
 | 
		
	
		
			
			|  | 129 | +		DBGC ( &efifb, "EFIFB could not get font information: %s\n",
 | 
		
	
		
			
			|  | 130 | +		       strerror ( rc ) );
 | 
		
	
		
			
			|  | 131 | +		goto err_info;
 | 
		
	
		
			
			|  | 132 | +	}
 | 
		
	
		
			
			|  | 133 | +	assert ( info != NULL );
 | 
		
	
		
			
			|  | 134 | +	efifb.font.height = info->FontInfo.FontSize;
 | 
		
	
		
			
			|  | 135 | +
 | 
		
	
		
			
			|  | 136 | +	/* Allocate glyph data */
 | 
		
	
		
			
			|  | 137 | +	len = ( 256 * efifb.font.height * sizeof ( bitmask ) );
 | 
		
	
		
			
			|  | 138 | +	efifb.glyphs = umalloc ( len );
 | 
		
	
		
			
			|  | 139 | +	if ( ! efifb.glyphs ) {
 | 
		
	
		
			
			|  | 140 | +		rc = -ENOMEM;
 | 
		
	
		
			
			|  | 141 | +		goto err_alloc;
 | 
		
	
		
			
			|  | 142 | +	}
 | 
		
	
		
			
			|  | 143 | +	memset_user ( efifb.glyphs, 0, 0, len );
 | 
		
	
		
			
			|  | 144 | +
 | 
		
	
		
			
			|  | 145 | +	/* Get font data */
 | 
		
	
		
			
			|  | 146 | +	for ( character = 0 ; character < 256 ; character++ ) {
 | 
		
	
		
			
			|  | 147 | +
 | 
		
	
		
			
			|  | 148 | +		/* Skip non-printable characters */
 | 
		
	
		
			
			|  | 149 | +		if ( ! isprint ( character ) )
 | 
		
	
		
			
			|  | 150 | +			continue;
 | 
		
	
		
			
			|  | 151 | +
 | 
		
	
		
			
			|  | 152 | +		/* Get glyph */
 | 
		
	
		
			
			|  | 153 | +		blt = NULL;
 | 
		
	
		
			
			|  | 154 | +		if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont,
 | 
		
	
		
			
			|  | 155 | +							 character, info, &blt,
 | 
		
	
		
			
			|  | 156 | +							 NULL ) ) != 0 ) {
 | 
		
	
		
			
			|  | 157 | +			rc = -EEFI ( efirc );
 | 
		
	
		
			
			|  | 158 | +			DBGC ( &efifb, "EFIFB could not get glyph %d: %s\n",
 | 
		
	
		
			
			|  | 159 | +			       character, strerror ( rc ) );
 | 
		
	
		
			
			|  | 160 | +			continue;
 | 
		
	
		
			
			|  | 161 | +		}
 | 
		
	
		
			
			|  | 162 | +		assert ( blt != NULL );
 | 
		
	
		
			
			|  | 163 | +
 | 
		
	
		
			
			|  | 164 | +		/* Sanity check */
 | 
		
	
		
			
			|  | 165 | +		if ( blt->Width > 8 ) {
 | 
		
	
		
			
			|  | 166 | +			DBGC ( &efifb, "EFIFB glyph %d invalid width %d\n",
 | 
		
	
		
			
			|  | 167 | +			       character, blt->Width );
 | 
		
	
		
			
			|  | 168 | +			continue;
 | 
		
	
		
			
			|  | 169 | +		}
 | 
		
	
		
			
			|  | 170 | +		if ( blt->Height > efifb.font.height ) {
 | 
		
	
		
			
			|  | 171 | +			DBGC ( &efifb, "EFIFB glyph %d invalid height %d\n",
 | 
		
	
		
			
			|  | 172 | +			       character, blt->Height );
 | 
		
	
		
			
			|  | 173 | +			continue;
 | 
		
	
		
			
			|  | 174 | +		}
 | 
		
	
		
			
			|  | 175 | +
 | 
		
	
		
			
			|  | 176 | +		/* Convert glyph to bitmap */
 | 
		
	
		
			
			|  | 177 | +		pixel = blt->Image.Bitmap;
 | 
		
	
		
			
			|  | 178 | +		offset = ( character * efifb.font.height );
 | 
		
	
		
			
			|  | 179 | +		for ( y = 0 ; y < blt->Height ; y++ ) {
 | 
		
	
		
			
			|  | 180 | +			bitmask = 0;
 | 
		
	
		
			
			|  | 181 | +			for ( x = 0 ; x < blt->Width ; x++ ) {
 | 
		
	
		
			
			|  | 182 | +				bitmask = rol8 ( bitmask, 1 );
 | 
		
	
		
			
			|  | 183 | +				if ( pixel->Blue || pixel->Green || pixel->Red )
 | 
		
	
		
			
			|  | 184 | +					bitmask |= 0x01;
 | 
		
	
		
			
			|  | 185 | +				pixel++;
 | 
		
	
		
			
			|  | 186 | +			}
 | 
		
	
		
			
			|  | 187 | +			copy_to_user ( efifb.glyphs, offset++, &bitmask,
 | 
		
	
		
			
			|  | 188 | +				       sizeof ( bitmask ) );
 | 
		
	
		
			
			|  | 189 | +		}
 | 
		
	
		
			
			|  | 190 | +		bs->FreePool ( blt );
 | 
		
	
		
			
			|  | 191 | +	}
 | 
		
	
		
			
			|  | 192 | +
 | 
		
	
		
			
			|  | 193 | +	/* Free font information */
 | 
		
	
		
			
			|  | 194 | +	bs->FreePool ( info );
 | 
		
	
		
			
			|  | 195 | +
 | 
		
	
		
			
			|  | 196 | +	efifb.font.glyph = efifb_glyph;
 | 
		
	
		
			
			|  | 197 | +	return 0;
 | 
		
	
		
			
			|  | 198 | +
 | 
		
	
		
			
			|  | 199 | +	ufree ( efifb.glyphs );
 | 
		
	
		
			
			|  | 200 | + err_alloc:
 | 
		
	
		
			
			|  | 201 | +	bs->FreePool ( info );
 | 
		
	
		
			
			|  | 202 | + err_info:
 | 
		
	
		
			
			|  | 203 | +	return rc;
 | 
		
	
		
			
			|  | 204 | +}
 | 
		
	
		
			
			|  | 205 | +
 | 
		
	
		
			
			|  | 206 | +/**
 | 
		
	
		
			
			|  | 207 | + * Generate colour mapping for a single colour component
 | 
		
	
		
			
			|  | 208 | + *
 | 
		
	
		
			
			|  | 209 | + * @v mask		Mask value
 | 
		
	
		
			
			|  | 210 | + * @v scale		Scale value to fill in
 | 
		
	
		
			
			|  | 211 | + * @v lsb		LSB value to fill in
 | 
		
	
		
			
			|  | 212 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 213 | + */
 | 
		
	
		
			
			|  | 214 | +static int efifb_colour_map_mask ( uint32_t mask, uint8_t *scale,
 | 
		
	
		
			
			|  | 215 | +				   uint8_t *lsb ) {
 | 
		
	
		
			
			|  | 216 | +	uint32_t check;
 | 
		
	
		
			
			|  | 217 | +
 | 
		
	
		
			
			|  | 218 | +	/* Fill in LSB and scale */
 | 
		
	
		
			
			|  | 219 | +	*lsb = ( mask ? ( ffs ( mask ) - 1 ) : 0 );
 | 
		
	
		
			
			|  | 220 | +	*scale = ( mask ? ( 8 - ( fls ( mask ) - *lsb ) ) : 8 );
 | 
		
	
		
			
			|  | 221 | +
 | 
		
	
		
			
			|  | 222 | +	/* Check that original mask was contiguous */
 | 
		
	
		
			
			|  | 223 | +	check = ( ( 0xff >> *scale ) << *lsb );
 | 
		
	
		
			
			|  | 224 | +	if ( check != mask )
 | 
		
	
		
			
			|  | 225 | +		return -ENOTSUP;
 | 
		
	
		
			
			|  | 226 | +
 | 
		
	
		
			
			|  | 227 | +	return 0;
 | 
		
	
		
			
			|  | 228 | +}
 | 
		
	
		
			
			|  | 229 | +
 | 
		
	
		
			
			|  | 230 | +/**
 | 
		
	
		
			
			|  | 231 | + * Generate colour mapping
 | 
		
	
		
			
			|  | 232 | + *
 | 
		
	
		
			
			|  | 233 | + * @v info		EFI mode information
 | 
		
	
		
			
			|  | 234 | + * @v map		Colour mapping to fill in
 | 
		
	
		
			
			|  | 235 | + * @ret bpp		Number of bits per pixel, or negative error
 | 
		
	
		
			
			|  | 236 | + */
 | 
		
	
		
			
			|  | 237 | +static int efifb_colour_map ( EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info,
 | 
		
	
		
			
			|  | 238 | +			      struct fbcon_colour_map *map ) {
 | 
		
	
		
			
			|  | 239 | +	static EFI_PIXEL_BITMASK rgb_mask = {
 | 
		
	
		
			
			|  | 240 | +		0x000000ffUL, 0x0000ff00UL, 0x00ff0000UL, 0xff000000UL
 | 
		
	
		
			
			|  | 241 | +	};
 | 
		
	
		
			
			|  | 242 | +	static EFI_PIXEL_BITMASK bgr_mask = {
 | 
		
	
		
			
			|  | 243 | +		0x00ff0000UL, 0x0000ff00UL, 0x000000ffUL, 0xff000000UL
 | 
		
	
		
			
			|  | 244 | +	};
 | 
		
	
		
			
			|  | 245 | +	EFI_PIXEL_BITMASK *mask;
 | 
		
	
		
			
			|  | 246 | +	uint8_t reserved_scale;
 | 
		
	
		
			
			|  | 247 | +	uint8_t reserved_lsb;
 | 
		
	
		
			
			|  | 248 | +	int rc;
 | 
		
	
		
			
			|  | 249 | +
 | 
		
	
		
			
			|  | 250 | +	/* Determine applicable mask */
 | 
		
	
		
			
			|  | 251 | +	switch ( info->PixelFormat ) {
 | 
		
	
		
			
			|  | 252 | +	case PixelRedGreenBlueReserved8BitPerColor:
 | 
		
	
		
			
			|  | 253 | +		mask = &rgb_mask;
 | 
		
	
		
			
			|  | 254 | +		break;
 | 
		
	
		
			
			|  | 255 | +	case PixelBlueGreenRedReserved8BitPerColor:
 | 
		
	
		
			
			|  | 256 | +		mask = &bgr_mask;
 | 
		
	
		
			
			|  | 257 | +		break;
 | 
		
	
		
			
			|  | 258 | +	case PixelBitMask:
 | 
		
	
		
			
			|  | 259 | +		mask = &info->PixelInformation;
 | 
		
	
		
			
			|  | 260 | +		break;
 | 
		
	
		
			
			|  | 261 | +	default:
 | 
		
	
		
			
			|  | 262 | +		DBGC ( &efifb, "EFIFB unrecognised pixel format %d\n",
 | 
		
	
		
			
			|  | 263 | +		       info->PixelFormat );
 | 
		
	
		
			
			|  | 264 | +		return -ENOTSUP;
 | 
		
	
		
			
			|  | 265 | +	}
 | 
		
	
		
			
			|  | 266 | +
 | 
		
	
		
			
			|  | 267 | +	/* Map each colour component */
 | 
		
	
		
			
			|  | 268 | +	if ( ( rc = efifb_colour_map_mask ( mask->RedMask, &map->red_scale,
 | 
		
	
		
			
			|  | 269 | +					    &map->red_lsb ) ) != 0 )
 | 
		
	
		
			
			|  | 270 | +		return rc;
 | 
		
	
		
			
			|  | 271 | +	if ( ( rc = efifb_colour_map_mask ( mask->GreenMask, &map->green_scale,
 | 
		
	
		
			
			|  | 272 | +					    &map->green_lsb ) ) != 0 )
 | 
		
	
		
			
			|  | 273 | +		return rc;
 | 
		
	
		
			
			|  | 274 | +	if ( ( rc = efifb_colour_map_mask ( mask->BlueMask, &map->blue_scale,
 | 
		
	
		
			
			|  | 275 | +					    &map->blue_lsb ) ) != 0 )
 | 
		
	
		
			
			|  | 276 | +		return rc;
 | 
		
	
		
			
			|  | 277 | +	if ( ( rc = efifb_colour_map_mask ( mask->ReservedMask, &reserved_scale,
 | 
		
	
		
			
			|  | 278 | +					    &reserved_lsb ) ) != 0 )
 | 
		
	
		
			
			|  | 279 | +		return rc;
 | 
		
	
		
			
			|  | 280 | +
 | 
		
	
		
			
			|  | 281 | +	/* Calculate total number of bits per pixel */
 | 
		
	
		
			
			|  | 282 | +	return ( 32 - ( reserved_scale + map->red_scale + map->green_scale +
 | 
		
	
		
			
			|  | 283 | +			map->blue_scale ) );
 | 
		
	
		
			
			|  | 284 | +}
 | 
		
	
		
			
			|  | 285 | +
 | 
		
	
		
			
			|  | 286 | +/**
 | 
		
	
		
			
			|  | 287 | + * Select video mode
 | 
		
	
		
			
			|  | 288 | + *
 | 
		
	
		
			
			|  | 289 | + * @v min_width		Minimum required width (in pixels)
 | 
		
	
		
			
			|  | 290 | + * @v min_height	Minimum required height (in pixels)
 | 
		
	
		
			
			|  | 291 | + * @v min_bpp		Minimum required colour depth (in bits per pixel)
 | 
		
	
		
			
			|  | 292 | + * @ret mode_number	Mode number, or negative error
 | 
		
	
		
			
			|  | 293 | + */
 | 
		
	
		
			
			|  | 294 | +static int efifb_select_mode ( unsigned int min_width, unsigned int min_height,
 | 
		
	
		
			
			|  | 295 | +			       unsigned int min_bpp ) {
 | 
		
	
		
			
			|  | 296 | +	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
 | 
		
	
		
			
			|  | 297 | +	struct fbcon_colour_map map;
 | 
		
	
		
			
			|  | 298 | +	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
 | 
		
	
		
			
			|  | 299 | +	int best_mode_number = -ENOENT;
 | 
		
	
		
			
			|  | 300 | +	unsigned int best_score = INT_MAX;
 | 
		
	
		
			
			|  | 301 | +	unsigned int score;
 | 
		
	
		
			
			|  | 302 | +	unsigned int mode;
 | 
		
	
		
			
			|  | 303 | +	int bpp;
 | 
		
	
		
			
			|  | 304 | +	UINTN size;
 | 
		
	
		
			
			|  | 305 | +	EFI_STATUS efirc;
 | 
		
	
		
			
			|  | 306 | +	int rc;
 | 
		
	
		
			
			|  | 307 | +
 | 
		
	
		
			
			|  | 308 | +	/* Find the best mode */
 | 
		
	
		
			
			|  | 309 | +	for ( mode = 0 ; mode < efifb.gop->Mode->MaxMode ; mode++ ) {
 | 
		
	
		
			
			|  | 310 | +
 | 
		
	
		
			
			|  | 311 | +		/* Get mode information */
 | 
		
	
		
			
			|  | 312 | +		if ( ( efirc = efifb.gop->QueryMode ( efifb.gop, mode, &size,
 | 
		
	
		
			
			|  | 313 | +						      &info ) ) != 0 ) {
 | 
		
	
		
			
			|  | 314 | +			rc = -EEFI ( efirc );
 | 
		
	
		
			
			|  | 315 | +			DBGC ( &efifb, "EFIFB could not get mode %d "
 | 
		
	
		
			
			|  | 316 | +			       "information: %s\n", mode, strerror ( rc ) );
 | 
		
	
		
			
			|  | 317 | +			goto err_query;
 | 
		
	
		
			
			|  | 318 | +		}
 | 
		
	
		
			
			|  | 319 | +
 | 
		
	
		
			
			|  | 320 | +		/* Skip unusable modes */
 | 
		
	
		
			
			|  | 321 | +		bpp = efifb_colour_map ( info, &map );
 | 
		
	
		
			
			|  | 322 | +		if ( bpp < 0 ) {
 | 
		
	
		
			
			|  | 323 | +			rc = bpp;
 | 
		
	
		
			
			|  | 324 | +			DBGC ( &efifb, "EFIFB could not build colour map for "
 | 
		
	
		
			
			|  | 325 | +			       "mode %d: %s\n", mode, strerror ( rc ) );
 | 
		
	
		
			
			|  | 326 | +			goto err_map;
 | 
		
	
		
			
			|  | 327 | +		}
 | 
		
	
		
			
			|  | 328 | +
 | 
		
	
		
			
			|  | 329 | +		/* Skip modes not meeting the requirements */
 | 
		
	
		
			
			|  | 330 | +		if ( ( info->HorizontalResolution < min_width ) ||
 | 
		
	
		
			
			|  | 331 | +		     ( info->VerticalResolution < min_height ) ||
 | 
		
	
		
			
			|  | 332 | +		     ( ( ( unsigned int ) bpp ) < min_bpp ) ) {
 | 
		
	
		
			
			|  | 333 | +			goto err_requirements;
 | 
		
	
		
			
			|  | 334 | +		}
 | 
		
	
		
			
			|  | 335 | +
 | 
		
	
		
			
			|  | 336 | +		/* Select this mode if it has the best (i.e. lowest)
 | 
		
	
		
			
			|  | 337 | +		 * score.  We choose the scoring system to favour
 | 
		
	
		
			
			|  | 338 | +		 * modes close to the specified width and height;
 | 
		
	
		
			
			|  | 339 | +		 * within modes of the same width and height we prefer
 | 
		
	
		
			
			|  | 340 | +		 * a higher colour depth.
 | 
		
	
		
			
			|  | 341 | +		 */
 | 
		
	
		
			
			|  | 342 | +		score = ( ( info->HorizontalResolution *
 | 
		
	
		
			
			|  | 343 | +			    info->VerticalResolution ) - bpp );
 | 
		
	
		
			
			|  | 344 | +		if ( score < best_score ) {
 | 
		
	
		
			
			|  | 345 | +			best_mode_number = mode;
 | 
		
	
		
			
			|  | 346 | +			best_score = score;
 | 
		
	
		
			
			|  | 347 | +		}
 | 
		
	
		
			
			|  | 348 | +
 | 
		
	
		
			
			|  | 349 | +	err_requirements:
 | 
		
	
		
			
			|  | 350 | +	err_map:
 | 
		
	
		
			
			|  | 351 | +		bs->FreePool ( info );
 | 
		
	
		
			
			|  | 352 | +	err_query:
 | 
		
	
		
			
			|  | 353 | +		continue;
 | 
		
	
		
			
			|  | 354 | +	}
 | 
		
	
		
			
			|  | 355 | +
 | 
		
	
		
			
			|  | 356 | +	if ( best_mode_number < 0 )
 | 
		
	
		
			
			|  | 357 | +		DBGC ( &efifb, "EFIFB found no suitable mode\n" );
 | 
		
	
		
			
			|  | 358 | +	return best_mode_number;
 | 
		
	
		
			
			|  | 359 | +}
 | 
		
	
		
			
			|  | 360 | +
 | 
		
	
		
			
			|  | 361 | +/**
 | 
		
	
		
			
			|  | 362 | + * Restore video mode
 | 
		
	
		
			
			|  | 363 | + *
 | 
		
	
		
			
			|  | 364 | + * @v rc		Return status code
 | 
		
	
		
			
			|  | 365 | + */
 | 
		
	
		
			
			|  | 366 | +static int efifb_restore ( void ) {
 | 
		
	
		
			
			|  | 367 | +	EFI_STATUS efirc;
 | 
		
	
		
			
			|  | 368 | +	int rc;
 | 
		
	
		
			
			|  | 369 | +
 | 
		
	
		
			
			|  | 370 | +	/* Restore original mode */
 | 
		
	
		
			
			|  | 371 | +	if ( ( efirc = efifb.gop->SetMode ( efifb.gop,
 | 
		
	
		
			
			|  | 372 | +					    efifb.saved_mode ) ) != 0 ) {
 | 
		
	
		
			
			|  | 373 | +		rc = -EEFI ( efirc );
 | 
		
	
		
			
			|  | 374 | +		DBGC ( &efifb, "EFIFB could not restore mode %d: %s\n",
 | 
		
	
		
			
			|  | 375 | +		       efifb.saved_mode, strerror ( rc ) );
 | 
		
	
		
			
			|  | 376 | +		return rc;
 | 
		
	
		
			
			|  | 377 | +	}
 | 
		
	
		
			
			|  | 378 | +
 | 
		
	
		
			
			|  | 379 | +	return 0;
 | 
		
	
		
			
			|  | 380 | +}
 | 
		
	
		
			
			|  | 381 | +
 | 
		
	
		
			
			|  | 382 | +/**
 | 
		
	
		
			
			|  | 383 | + * Initialise EFI frame buffer
 | 
		
	
		
			
			|  | 384 | + *
 | 
		
	
		
			
			|  | 385 | + * @v config		Console configuration, or NULL to reset
 | 
		
	
		
			
			|  | 386 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 387 | + */
 | 
		
	
		
			
			|  | 388 | +static int efifb_init ( struct console_configuration *config ) {
 | 
		
	
		
			
			|  | 389 | +	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
 | 
		
	
		
			
			|  | 390 | +	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
 | 
		
	
		
			
			|  | 391 | +	void *interface;
 | 
		
	
		
			
			|  | 392 | +	int mode;
 | 
		
	
		
			
			|  | 393 | +	int bpp;
 | 
		
	
		
			
			|  | 394 | +	EFI_STATUS efirc;
 | 
		
	
		
			
			|  | 395 | +	int rc;
 | 
		
	
		
			
			|  | 396 | +
 | 
		
	
		
			
			|  | 397 | +	/* Locate graphics output protocol */
 | 
		
	
		
			
			|  | 398 | +	if ( ( efirc = bs->LocateProtocol ( &efi_graphics_output_protocol_guid,
 | 
		
	
		
			
			|  | 399 | +					    NULL, &interface ) ) != 0 ) {
 | 
		
	
		
			
			|  | 400 | +		rc = -EEFI ( efirc );
 | 
		
	
		
			
			|  | 401 | +		DBGC ( &efifb, "EFIFB could not locate graphics output "
 | 
		
	
		
			
			|  | 402 | +		       "protocol: %s\n", strerror ( rc ) );
 | 
		
	
		
			
			|  | 403 | +		goto err_locate_gop;
 | 
		
	
		
			
			|  | 404 | +	}
 | 
		
	
		
			
			|  | 405 | +	efifb.gop = interface;
 | 
		
	
		
			
			|  | 406 | +
 | 
		
	
		
			
			|  | 407 | +	/* Locate HII font protocol */
 | 
		
	
		
			
			|  | 408 | +	if ( ( efirc = bs->LocateProtocol ( &efi_hii_font_protocol_guid,
 | 
		
	
		
			
			|  | 409 | +					    NULL, &interface ) ) != 0 ) {
 | 
		
	
		
			
			|  | 410 | +		rc = -EEFI ( efirc );
 | 
		
	
		
			
			|  | 411 | +		DBGC ( &efifb, "EFIFB could not locate HII font protocol: %s\n",
 | 
		
	
		
			
			|  | 412 | +		       strerror ( rc ) );
 | 
		
	
		
			
			|  | 413 | +		goto err_locate_hiifont;
 | 
		
	
		
			
			|  | 414 | +	}
 | 
		
	
		
			
			|  | 415 | +	efifb.hiifont = interface;
 | 
		
	
		
			
			|  | 416 | +
 | 
		
	
		
			
			|  | 417 | +	/* Locate glyphs */
 | 
		
	
		
			
			|  | 418 | +	if ( ( rc = efifb_glyphs() ) != 0 )
 | 
		
	
		
			
			|  | 419 | +		goto err_glyphs;
 | 
		
	
		
			
			|  | 420 | +
 | 
		
	
		
			
			|  | 421 | +	/* Save original mode */
 | 
		
	
		
			
			|  | 422 | +	efifb.saved_mode = efifb.gop->Mode->Mode;
 | 
		
	
		
			
			|  | 423 | +
 | 
		
	
		
			
			|  | 424 | +	/* Select mode */
 | 
		
	
		
			
			|  | 425 | +	if ( ( mode = efifb_select_mode ( config->width, config->height,
 | 
		
	
		
			
			|  | 426 | +					  config->depth ) ) < 0 ) {
 | 
		
	
		
			
			|  | 427 | +		rc = mode;
 | 
		
	
		
			
			|  | 428 | +		goto err_select_mode;
 | 
		
	
		
			
			|  | 429 | +	}
 | 
		
	
		
			
			|  | 430 | +
 | 
		
	
		
			
			|  | 431 | +	/* Set mode */
 | 
		
	
		
			
			|  | 432 | +	if ( ( efirc = efifb.gop->SetMode ( efifb.gop, mode ) ) != 0 ) {
 | 
		
	
		
			
			|  | 433 | +		rc = -EEFI ( efirc );
 | 
		
	
		
			
			|  | 434 | +		DBGC ( &efifb, "EFIFB could not set mode %d: %s\n",
 | 
		
	
		
			
			|  | 435 | +		       mode, strerror ( rc ) );
 | 
		
	
		
			
			|  | 436 | +		goto err_set_mode;
 | 
		
	
		
			
			|  | 437 | +	}
 | 
		
	
		
			
			|  | 438 | +	info = efifb.gop->Mode->Info;
 | 
		
	
		
			
			|  | 439 | +
 | 
		
	
		
			
			|  | 440 | +	/* Populate colour map */
 | 
		
	
		
			
			|  | 441 | +	bpp = efifb_colour_map ( info, &efifb.map );
 | 
		
	
		
			
			|  | 442 | +	if ( bpp < 0 ) {
 | 
		
	
		
			
			|  | 443 | +		rc = bpp;
 | 
		
	
		
			
			|  | 444 | +		DBGC ( &efifb, "EFIFB could not build colour map for "
 | 
		
	
		
			
			|  | 445 | +		       "mode %d: %s\n", mode, strerror ( rc ) );
 | 
		
	
		
			
			|  | 446 | +		goto err_map;
 | 
		
	
		
			
			|  | 447 | +	}
 | 
		
	
		
			
			|  | 448 | +
 | 
		
	
		
			
			|  | 449 | +	/* Populate pixel geometry */
 | 
		
	
		
			
			|  | 450 | +	efifb.pixel.width = info->HorizontalResolution;
 | 
		
	
		
			
			|  | 451 | +	efifb.pixel.height = info->VerticalResolution;
 | 
		
	
		
			
			|  | 452 | +	efifb.pixel.len = ( ( bpp + 7 ) / 8 );
 | 
		
	
		
			
			|  | 453 | +	efifb.pixel.stride = ( efifb.pixel.len * info->PixelsPerScanLine );
 | 
		
	
		
			
			|  | 454 | +
 | 
		
	
		
			
			|  | 455 | +	/* Populate frame buffer address */
 | 
		
	
		
			
			|  | 456 | +	efifb.start = efifb.gop->Mode->FrameBufferBase;
 | 
		
	
		
			
			|  | 457 | +	DBGC ( &efifb, "EFIFB using mode %d (%dx%d %dbpp at %#08lx)\n",
 | 
		
	
		
			
			|  | 458 | +	       mode, efifb.pixel.width, efifb.pixel.height, bpp, efifb.start );
 | 
		
	
		
			
			|  | 459 | +
 | 
		
	
		
			
			|  | 460 | +	/* Initialise frame buffer console */
 | 
		
	
		
			
			|  | 461 | +	if ( ( rc = fbcon_init ( &efifb.fbcon, phys_to_user ( efifb.start ),
 | 
		
	
		
			
			|  | 462 | +				 &efifb.pixel, &efifb.map, &efifb.font,
 | 
		
	
		
			
			|  | 463 | +				 config ) ) != 0 )
 | 
		
	
		
			
			|  | 464 | +		goto err_fbcon_init;
 | 
		
	
		
			
			|  | 465 | +
 | 
		
	
		
			
			|  | 466 | +	return 0;
 | 
		
	
		
			
			|  | 467 | +
 | 
		
	
		
			
			|  | 468 | +	fbcon_fini ( &efifb.fbcon );
 | 
		
	
		
			
			|  | 469 | + err_fbcon_init:
 | 
		
	
		
			
			|  | 470 | + err_map:
 | 
		
	
		
			
			|  | 471 | +	efifb_restore();
 | 
		
	
		
			
			|  | 472 | + err_set_mode:
 | 
		
	
		
			
			|  | 473 | + err_select_mode:
 | 
		
	
		
			
			|  | 474 | +	ufree ( efifb.glyphs );
 | 
		
	
		
			
			|  | 475 | + err_glyphs:
 | 
		
	
		
			
			|  | 476 | + err_locate_hiifont:
 | 
		
	
		
			
			|  | 477 | + err_locate_gop:
 | 
		
	
		
			
			|  | 478 | +	return rc;
 | 
		
	
		
			
			|  | 479 | +}
 | 
		
	
		
			
			|  | 480 | +
 | 
		
	
		
			
			|  | 481 | +/**
 | 
		
	
		
			
			|  | 482 | + * Finalise EFI frame buffer
 | 
		
	
		
			
			|  | 483 | + *
 | 
		
	
		
			
			|  | 484 | + */
 | 
		
	
		
			
			|  | 485 | +static void efifb_fini ( void ) {
 | 
		
	
		
			
			|  | 486 | +
 | 
		
	
		
			
			|  | 487 | +	/* Finalise frame buffer console */
 | 
		
	
		
			
			|  | 488 | +	fbcon_fini ( &efifb.fbcon );
 | 
		
	
		
			
			|  | 489 | +
 | 
		
	
		
			
			|  | 490 | +	/* Restore saved mode */
 | 
		
	
		
			
			|  | 491 | +	efifb_restore();
 | 
		
	
		
			
			|  | 492 | +
 | 
		
	
		
			
			|  | 493 | +	/* Free glyphs */
 | 
		
	
		
			
			|  | 494 | +	ufree ( efifb.glyphs );
 | 
		
	
		
			
			|  | 495 | +}
 | 
		
	
		
			
			|  | 496 | +
 | 
		
	
		
			
			|  | 497 | +/**
 | 
		
	
		
			
			|  | 498 | + * Print a character to current cursor position
 | 
		
	
		
			
			|  | 499 | + *
 | 
		
	
		
			
			|  | 500 | + * @v character		Character
 | 
		
	
		
			
			|  | 501 | + */
 | 
		
	
		
			
			|  | 502 | +static void efifb_putchar ( int character ) {
 | 
		
	
		
			
			|  | 503 | +
 | 
		
	
		
			
			|  | 504 | +	fbcon_putchar ( &efifb.fbcon, character );
 | 
		
	
		
			
			|  | 505 | +}
 | 
		
	
		
			
			|  | 506 | +
 | 
		
	
		
			
			|  | 507 | +/**
 | 
		
	
		
			
			|  | 508 | + * Configure console
 | 
		
	
		
			
			|  | 509 | + *
 | 
		
	
		
			
			|  | 510 | + * @v config		Console configuration, or NULL to reset
 | 
		
	
		
			
			|  | 511 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 512 | + */
 | 
		
	
		
			
			|  | 513 | +static int efifb_configure ( struct console_configuration *config ) {
 | 
		
	
		
			
			|  | 514 | +	int rc;
 | 
		
	
		
			
			|  | 515 | +
 | 
		
	
		
			
			|  | 516 | +	/* Reset console, if applicable */
 | 
		
	
		
			
			|  | 517 | +	if ( ! efifb_console.disabled ) {
 | 
		
	
		
			
			|  | 518 | +		efifb_fini();
 | 
		
	
		
			
			|  | 519 | +		efi_console.disabled &= ~CONSOLE_DISABLED_OUTPUT;
 | 
		
	
		
			
			|  | 520 | +		ansicol_reset_magic();
 | 
		
	
		
			
			|  | 521 | +	}
 | 
		
	
		
			
			|  | 522 | +	efifb_console.disabled = CONSOLE_DISABLED;
 | 
		
	
		
			
			|  | 523 | +
 | 
		
	
		
			
			|  | 524 | +	/* Do nothing more unless we have a usable configuration */
 | 
		
	
		
			
			|  | 525 | +	if ( ( config == NULL ) ||
 | 
		
	
		
			
			|  | 526 | +	     ( config->width == 0 ) || ( config->height == 0 ) ) {
 | 
		
	
		
			
			|  | 527 | +		return 0;
 | 
		
	
		
			
			|  | 528 | +	}
 | 
		
	
		
			
			|  | 529 | +
 | 
		
	
		
			
			|  | 530 | +	/* Initialise EFI frame buffer */
 | 
		
	
		
			
			|  | 531 | +	if ( ( rc = efifb_init ( config ) ) != 0 )
 | 
		
	
		
			
			|  | 532 | +		return rc;
 | 
		
	
		
			
			|  | 533 | +
 | 
		
	
		
			
			|  | 534 | +	/* Mark console as enabled */
 | 
		
	
		
			
			|  | 535 | +	efifb_console.disabled = 0;
 | 
		
	
		
			
			|  | 536 | +	efi_console.disabled |= CONSOLE_DISABLED_OUTPUT;
 | 
		
	
		
			
			|  | 537 | +
 | 
		
	
		
			
			|  | 538 | +	/* Set magic colour to transparent if we have a background picture */
 | 
		
	
		
			
			|  | 539 | +	if ( config->pixbuf )
 | 
		
	
		
			
			|  | 540 | +		ansicol_set_magic_transparent();
 | 
		
	
		
			
			|  | 541 | +
 | 
		
	
		
			
			|  | 542 | +	return 0;
 | 
		
	
		
			
			|  | 543 | +}
 | 
		
	
		
			
			|  | 544 | +
 | 
		
	
		
			
			|  | 545 | +/** EFI graphics output protocol console driver */
 | 
		
	
		
			
			|  | 546 | +struct console_driver efifb_console __console_driver = {
 | 
		
	
		
			
			|  | 547 | +	.usage = CONSOLE_EFIFB,
 | 
		
	
		
			
			|  | 548 | +	.putchar = efifb_putchar,
 | 
		
	
		
			
			|  | 549 | +	.configure = efifb_configure,
 | 
		
	
		
			
			|  | 550 | +	.disabled = CONSOLE_DISABLED,
 | 
		
	
		
			
			|  | 551 | +};
 |