efi_loader: support Unicode text input

Up to now the EFI_TEXT_INPUT_PROTOCOL only supported ASCII characters.
With the patch it can consume UTF-8 from the console.

Currently only the serial console and the console can deliver UTF-8.
Local consoles are restricted to ASCII.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Signed-off-by: Alexander Graf <agraf@suse.de>
diff --git a/lib/charset.c b/lib/charset.c
index 72c808c..0cede9b 100644
--- a/lib/charset.c
+++ b/lib/charset.c
@@ -5,6 +5,7 @@
  *  Copyright (c) 2017 Rob Clark
  */
 
+#include <common.h>
 #include <charset.h>
 #include <capitalization.h>
 #include <malloc.h>
@@ -18,67 +19,107 @@
 	CP437_CAPITALIZATION_TABLE;
 #endif
 
-s32 utf8_get(const char **src)
+/**
+ * get_code() - read Unicode code point from UTF-8 stream
+ *
+ * @read_u8:	- stream reader
+ * @src:	- string buffer passed to stream reader, optional
+ * Return:	- Unicode code point
+ */
+static int get_code(u8 (*read_u8)(void *data), void *data)
 {
-	s32 code = 0;
-	unsigned char c;
+	s32 ch = 0;
 
-	if (!src || !*src)
-		return -1;
-	if (!**src)
+	ch = read_u8(data);
+	if (!ch)
 		return 0;
-	c = **src;
-	if (c >= 0x80) {
-		++*src;
-		if (!**src)
-			return -1;
-		/*
-		 * We do not expect a continuation byte (0x80 - 0xbf).
-		 * 0x80 is coded as 0xc2 0x80, so we cannot have less then 0xc2
-		 * here.
-		 * The highest code point is 0x10ffff which is coded as
-		 * 0xf4 0x8f 0xbf 0xbf. So we cannot have a byte above 0xf4.
-		 */
-		if (c < 0xc2 || code > 0xf4)
-			return -1;
-		if (c >= 0xe0) {
-			if (c >= 0xf0) {
+	if (ch >= 0xc2 && ch <= 0xf4) {
+		int code = 0;
+
+		if (ch >= 0xe0) {
+			if (ch >= 0xf0) {
 				/* 0xf0 - 0xf4 */
-				c &= 0x07;
-				code = c << 18;
-				c = **src;
-				++*src;
-				if (!**src)
-					return -1;
-				if (c < 0x80 || c > 0xbf)
-					return -1;
-				c &= 0x3f;
+				ch &= 0x07;
+				code = ch << 18;
+				ch = read_u8(data);
+				if (ch < 0x80 || ch > 0xbf)
+					goto error;
+				ch &= 0x3f;
 			} else {
 				/* 0xe0 - 0xef */
-				c &= 0x0f;
+				ch &= 0x0f;
 			}
-			code += c << 12;
+			code += ch << 12;
 			if ((code >= 0xD800 && code <= 0xDFFF) ||
 			    code >= 0x110000)
-				return -1;
-			c = **src;
-			++*src;
-			if (!**src)
-				return -1;
-			if (c < 0x80 || c > 0xbf)
-				return -1;
+				goto error;
+			ch = read_u8(data);
+			if (ch < 0x80 || ch > 0xbf)
+				goto error;
 		}
 		/* 0xc0 - 0xdf or continuation byte (0x80 - 0xbf) */
-		c &= 0x3f;
-		code += c << 6;
-		c = **src;
-		if (c < 0x80 || c > 0xbf)
-			return -1;
-		c &= 0x3f;
+		ch &= 0x3f;
+		code += ch << 6;
+		ch = read_u8(data);
+		if (ch < 0x80 || ch > 0xbf)
+			goto error;
+		ch &= 0x3f;
+		ch += code;
+	} else if (ch >= 0x80) {
+		goto error;
 	}
-	code += c;
+	return ch;
+error:
+	return '?';
+}
+
+/**
+ * read_string() - read byte from character string
+ *
+ * @data:	- pointer to string
+ * Return:	- byte read
+ *
+ * The string pointer is incremented if it does not point to '\0'.
+ */
+static u8 read_string(void *data)
+
+{
+	const char **src = (const char **)data;
+	u8 c;
+
+	if (!src || !*src || !**src)
+		return 0;
+	c = **src;
 	++*src;
-	return code;
+	return c;
+}
+
+/**
+ * read_console() - read byte from console
+ *
+ * @src		- not used, needed to match interface
+ * Return:	- byte read
+ */
+static u8 read_console(void *data)
+{
+	return getc();
+}
+
+int console_read_unicode(s32 *code)
+{
+	if (!tstc()) {
+		/* No input available */
+		return 1;
+	}
+
+	/* Read Unicode code */
+	*code = get_code(read_console, NULL);
+	return 0;
+}
+
+s32 utf8_get(const char **src)
+{
+	return get_code(read_string, src);
 }
 
 int utf8_put(s32 code, char **dst)