[fbcon] Support Unicode console output

Accumulate UTF-8 characters in fbcon_putchar(), and require the frame
buffer console's .glyph() method to accept Unicode character values.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
diff --git a/src/arch/x86/interface/pcbios/vesafb.c b/src/arch/x86/interface/pcbios/vesafb.c
index 50e4858..d49832d 100644
--- a/src/arch/x86/interface/pcbios/vesafb.c
+++ b/src/arch/x86/interface/pcbios/vesafb.c
@@ -78,6 +78,15 @@
 /** Font corresponding to selected character width and height */
 #define VESAFB_FONT VBE_FONT_8x16
 
+/** Number of ASCII glyphs within the font */
+#define VESAFB_ASCII 128
+
+/** Glyph to render for non-ASCII characters
+ *
+ * We choose to use one of the box-drawing glyphs.
+ */
+#define VESAFB_UNKNOWN 0xb1
+
 /* Forward declaration */
 struct console_driver vesafb_console __console_driver;
 
@@ -130,12 +139,24 @@
 /**
  * Get character glyph
  *
- * @v character		Character
+ * @v character		Unicode character
  * @v glyph		Character glyph to fill in
  */
 static void vesafb_glyph ( unsigned int character, uint8_t *glyph ) {
-	size_t offset = ( character * VESAFB_CHAR_HEIGHT );
+	unsigned int index;
+	size_t offset;
 
+	/* Identify glyph */
+	if ( character < VESAFB_ASCII ) {
+		/* ASCII character: use corresponding glyph */
+		index = character;
+	} else {
+		/* Non-ASCII character: use "unknown" glyph */
+		index = VESAFB_UNKNOWN;
+	}
+
+	/* Copy glyph from BIOS font table */
+	offset = ( index * VESAFB_CHAR_HEIGHT );
 	copy_from_real ( glyph, vesafb.glyphs.segment,
 			 ( vesafb.glyphs.offset + offset ), VESAFB_CHAR_HEIGHT);
 }
diff --git a/src/core/fbcon.c b/src/core/fbcon.c
index 44a56e1..ad97472 100644
--- a/src/core/fbcon.c
+++ b/src/core/fbcon.c
@@ -446,6 +446,10 @@
 	if ( character < 0 )
 		return;
 
+	/* Accumulate Unicode characters */
+	if ( ( character = utf8_accumulate ( &fbcon->utf8, character ) ) == 0 )
+		return;
+
 	/* Handle control characters */
 	switch ( character ) {
 	case '\r':
diff --git a/src/include/ipxe/fbcon.h b/src/include/ipxe/fbcon.h
index 42ffca3..a4c7a9a 100644
--- a/src/include/ipxe/fbcon.h
+++ b/src/include/ipxe/fbcon.h
@@ -11,6 +11,7 @@
 
 #include <stdint.h>
 #include <ipxe/ansiesc.h>
+#include <ipxe/utf8.h>
 #include <ipxe/uaccess.h>
 #include <ipxe/console.h>
 
@@ -36,7 +37,7 @@
 	/**
 	 * Get character glyph
 	 *
-	 * @v character		Character
+	 * @v character		Unicode character
 	 * @v glyph		Character glyph to fill in
 	 */
 	void ( * glyph ) ( unsigned int character, uint8_t *glyph );
@@ -92,7 +93,7 @@
 	uint32_t foreground;
 	/** Background colour */
 	uint32_t background;
-	/** Character */
+	/** Unicode character */
 	unsigned int character;
 };
 
@@ -138,6 +139,8 @@
 	unsigned int ypos;
 	/** ANSI escape sequence context */
 	struct ansiesc_context ctx;
+	/** UTF-8 accumulator */
+	struct utf8_accumulator utf8;
 	/** Text array */
 	struct fbcon_text text;
 	/** Background picture */
diff --git a/src/interface/efi/efi_fbcon.c b/src/interface/efi/efi_fbcon.c
index abc5a93..d9e3e69 100644
--- a/src/interface/efi/efi_fbcon.c
+++ b/src/interface/efi/efi_fbcon.c
@@ -62,6 +62,9 @@
 #define CONSOLE_EFIFB ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
 #endif
 
+/** Number of ASCII glyphs in cache */
+#define EFIFB_ASCII 128
+
 /* Forward declaration */
 struct console_driver efifb_console __console_driver;
 
@@ -84,7 +87,7 @@
 	struct fbcon_colour_map map;
 	/** Font definition */
 	struct fbcon_font font;
-	/** Character glyphs */
+	/** Character glyph cache */
 	userptr_t glyphs;
 };
 
@@ -92,14 +95,112 @@
 static struct efifb efifb;
 
 /**
- * Get character glyph
+ * Draw character glyph
  *
  * @v character		Character
+ * @v index		Index within glyph cache
+ * @v toggle		Bits to toggle in each bitmask
+ * @ret height		Character height, or negative error
+ */
+static int efifb_draw ( unsigned int character, unsigned int index,
+			unsigned int toggle ) {
+	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+	EFI_IMAGE_OUTPUT *blt;
+	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixel;
+	unsigned int height;
+	unsigned int x;
+	unsigned int y;
+	uint8_t bitmask;
+	size_t offset;
+	EFI_STATUS efirc;
+	int rc;
+
+	/* Clear existing glyph */
+	offset = ( index * efifb.font.height );
+	memset_user ( efifb.glyphs, offset, 0, efifb.font.height );
+
+	/* Get glyph */
+	blt = NULL;
+	if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont, character,
+						 NULL, &blt, NULL ) ) != 0 ) {
+		rc = -EEFI ( efirc );
+		DBGC ( &efifb, "EFIFB could not get glyph %#02x: %s\n",
+		       character, strerror ( rc ) );
+		goto err_get;
+	}
+	assert ( blt != NULL );
+
+	/* Sanity check */
+	if ( blt->Width > 8 ) {
+		DBGC ( &efifb, "EFIFB glyph %#02x invalid width %d\n",
+		       character, blt->Width );
+		rc = -EINVAL;
+		goto err_width;
+	}
+
+	/* Convert glyph to bitmap */
+	pixel = blt->Image.Bitmap;
+	height = blt->Height;
+	for ( y = 0 ; ( ( y < height ) && ( y < efifb.font.height ) ) ; y++ ) {
+		bitmask = 0;
+		for ( x = 0 ; x < blt->Width ; x++ ) {
+			bitmask = rol8 ( bitmask, 1 );
+			if ( pixel->Blue || pixel->Green || pixel->Red )
+				bitmask |= 0x01;
+			pixel++;
+		}
+		bitmask ^= toggle;
+		copy_to_user ( efifb.glyphs, offset++, &bitmask,
+			       sizeof ( bitmask ) );
+	}
+
+	/* Free glyph */
+	bs->FreePool ( blt );
+
+	return height;
+
+ err_width:
+	bs->FreePool ( blt );
+ err_get:
+	return rc;
+}
+
+/**
+ * Draw "unknown character" glyph
+ *
+ * @v index		Index within glyph cache
+ * @ret rc		Return status code
+ */
+static int efifb_draw_unknown ( unsigned int index ) {
+
+	/* Draw an inverted '?' glyph */
+	return efifb_draw ( '?', index, -1U );
+}
+
+/**
+ * Get character glyph
+ *
+ * @v character		Unicode character
  * @v glyph		Character glyph to fill in
  */
 static void efifb_glyph ( unsigned int character, uint8_t *glyph ) {
-	size_t offset = ( character * efifb.font.height );
+	unsigned int index;
+	size_t offset;
 
+	/* Identify glyph */
+	if ( character < EFIFB_ASCII ) {
+
+		/* ASCII character: use fixed cache entry */
+		index = character;
+
+	} else {
+
+		/* Non-ASCII character: use an "unknown" glyph */
+		index = 0;
+	}
+
+	/* Copy cached glyph */
+	offset = ( index * efifb.font.height );
 	copy_from_user ( glyph, efifb.glyphs, offset, efifb.font.height );
 }
 
@@ -109,16 +210,10 @@
  * @ret rc		Return status code
  */
 static int efifb_glyphs ( void ) {
-	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
-	EFI_IMAGE_OUTPUT *blt;
-	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixel;
-	size_t offset;
-	size_t len;
-	uint8_t bitmask;
 	unsigned int character;
-	unsigned int x;
-	unsigned int y;
-	EFI_STATUS efirc;
+	int height;
+	int max;
+	size_t len;
 	int rc;
 
 	/* Get font height.  The GetFontInfo() call nominally returns
@@ -128,38 +223,32 @@
 	 * height.
 	 */
 	efifb.font.height = 0;
-	for ( character = 0 ; character < 256 ; character++ ) {
+	max = 0;
+	for ( character = 0 ; character < EFIFB_ASCII ; character++ ) {
 
 		/* Skip non-printable characters */
 		if ( ! isprint ( character ) )
 			continue;
 
 		/* Get glyph */
-		blt = NULL;
-		if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont,
-							 character, NULL, &blt,
-							 NULL ) ) != 0 ) {
-			rc = -EEFI ( efirc );
-			DBGC ( &efifb, "EFIFB could not get glyph %d: %s\n",
-			       character, strerror ( rc ) );
-			continue;
+		height = efifb_draw ( character, 0, 0 );
+		if ( height < 0 ) {
+			rc = height;
+			goto err_height;
 		}
-		assert ( blt != NULL );
 
 		/* Calculate maximum height */
-		if ( efifb.font.height < blt->Height )
-			efifb.font.height = blt->Height;
-
-		/* Free glyph */
-		bs->FreePool ( blt );
+		if ( max < height )
+			max = height;
 	}
-	if ( ! efifb.font.height ) {
+	if ( ! max ) {
 		DBGC ( &efifb, "EFIFB could not get font height\n" );
 		return -ENOENT;
 	}
+	efifb.font.height = max;
 
 	/* Allocate glyph data */
-	len = ( 256 * efifb.font.height * sizeof ( bitmask ) );
+	len = ( EFIFB_ASCII * efifb.font.height );
 	efifb.glyphs = umalloc ( len );
 	if ( ! efifb.glyphs ) {
 		rc = -ENOMEM;
@@ -168,60 +257,29 @@
 	memset_user ( efifb.glyphs, 0, 0, len );
 
 	/* Get font data */
-	for ( character = 0 ; character < 256 ; character++ ) {
+	for ( character = 0 ; character < EFIFB_ASCII ; character++ ) {
 
 		/* Skip non-printable characters */
-		if ( ! isprint ( character ) )
+		if ( ! isprint ( character ) ) {
+			efifb_draw_unknown ( character );
 			continue;
+		}
 
 		/* Get glyph */
-		blt = NULL;
-		if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont,
-							 character, NULL, &blt,
-							 NULL ) ) != 0 ) {
-			rc = -EEFI ( efirc );
-			DBGC ( &efifb, "EFIFB could not get glyph %d: %s\n",
-			       character, strerror ( rc ) );
-			continue;
+		height = efifb_draw ( character, character, 0 );
+		if ( height < 0 ) {
+			rc = height;
+			goto err_draw;
 		}
-		assert ( blt != NULL );
-
-		/* Sanity check */
-		if ( blt->Width > 8 ) {
-			DBGC ( &efifb, "EFIFB glyph %d invalid width %d\n",
-			       character, blt->Width );
-			continue;
-		}
-		if ( blt->Height > efifb.font.height ) {
-			DBGC ( &efifb, "EFIFB glyph %d invalid height %d\n",
-			       character, blt->Height );
-			continue;
-		}
-
-		/* Convert glyph to bitmap */
-		pixel = blt->Image.Bitmap;
-		offset = ( character * efifb.font.height );
-		for ( y = 0 ; y < blt->Height ; y++ ) {
-			bitmask = 0;
-			for ( x = 0 ; x < blt->Width ; x++ ) {
-				bitmask = rol8 ( bitmask, 1 );
-				if ( pixel->Blue || pixel->Green || pixel->Red )
-					bitmask |= 0x01;
-				pixel++;
-			}
-			copy_to_user ( efifb.glyphs, offset++, &bitmask,
-				       sizeof ( bitmask ) );
-		}
-
-		/* Free glyph */
-		bs->FreePool ( blt );
 	}
 
 	efifb.font.glyph = efifb_glyph;
 	return 0;
 
+ err_draw:
 	ufree ( efifb.glyphs );
  err_alloc:
+ err_height:
 	return rc;
 }