Module Name:    src
Committed By:   martin
Date:           Mon Apr  2 08:50:33 UTC 2018

Modified Files:
        src/sys/arch/i386/stand/efiboot [netbsd-8]: Makefile.efiboot boot.c
            efiboot.c efiboot.h eficons.c efidelay.c efidisk.c efimemory.c
        src/sys/arch/i386/stand/efiboot/bootia32 [netbsd-8]: efibootia32.c
        src/sys/arch/i386/stand/efiboot/bootx64 [netbsd-8]: efibootx64.c
Added Files:
        src/sys/arch/i386/stand/efiboot [netbsd-8]: efichar.c

Log Message:
Pull up following revision(s) (requested by nonaka in ticket #685):

        sys/arch/i386/stand/efiboot/efidisk.c: revision 1.4
        sys/arch/i386/stand/efiboot/bootx64/efibootx64.c: revision 1.4
        sys/arch/i386/stand/efiboot/eficons.c: revision 1.5
        sys/arch/i386/stand/efiboot/efichar.c: revision 1.1
        sys/arch/i386/stand/efiboot/bootia32/efibootia32.c: revision 1.4
        sys/arch/i386/stand/efiboot/boot.c: revision 1.8
        sys/arch/i386/stand/efiboot/efimemory.c: revision 1.5
        sys/arch/i386/stand/efiboot/efiboot.c: revision 1.6
        sys/arch/i386/stand/efiboot/efidelay.c: revision 1.2
        sys/arch/i386/stand/efiboot/efiboot.h: revision 1.7
        sys/arch/i386/stand/efiboot/Makefile.efiboot: revision 1.11

efiboot: Added serial console support.


To generate a diff of this commit:
cvs rdiff -u -r1.9.2.1 -r1.9.2.2 \
    src/sys/arch/i386/stand/efiboot/Makefile.efiboot
cvs rdiff -u -r1.5.2.2 -r1.5.2.3 src/sys/arch/i386/stand/efiboot/boot.c
cvs rdiff -u -r1.4.10.1 -r1.4.10.2 src/sys/arch/i386/stand/efiboot/efiboot.c
cvs rdiff -u -r1.5.2.1 -r1.5.2.2 src/sys/arch/i386/stand/efiboot/efiboot.h
cvs rdiff -u -r0 -r1.1.4.2 src/sys/arch/i386/stand/efiboot/efichar.c
cvs rdiff -u -r1.4 -r1.4.2.1 src/sys/arch/i386/stand/efiboot/eficons.c
cvs rdiff -u -r1.1 -r1.1.12.1 src/sys/arch/i386/stand/efiboot/efidelay.c
cvs rdiff -u -r1.1.12.2 -r1.1.12.3 src/sys/arch/i386/stand/efiboot/efidisk.c
cvs rdiff -u -r1.4 -r1.4.10.1 src/sys/arch/i386/stand/efiboot/efimemory.c
cvs rdiff -u -r1.3 -r1.3.2.1 \
    src/sys/arch/i386/stand/efiboot/bootia32/efibootia32.c
cvs rdiff -u -r1.3 -r1.3.2.1 \
    src/sys/arch/i386/stand/efiboot/bootx64/efibootx64.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/i386/stand/efiboot/Makefile.efiboot
diff -u src/sys/arch/i386/stand/efiboot/Makefile.efiboot:1.9.2.1 src/sys/arch/i386/stand/efiboot/Makefile.efiboot:1.9.2.2
--- src/sys/arch/i386/stand/efiboot/Makefile.efiboot:1.9.2.1	Tue Mar 13 14:54:52 2018
+++ src/sys/arch/i386/stand/efiboot/Makefile.efiboot	Mon Apr  2 08:50:33 2018
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile.efiboot,v 1.9.2.1 2018/03/13 14:54:52 martin Exp $
+# $NetBSD: Makefile.efiboot,v 1.9.2.2 2018/04/02 08:50:33 martin Exp $
 
 S=		${.CURDIR}/../../../../..
 
@@ -12,7 +12,7 @@ SOURCES?= start.S conf.c devopen.c efibo
 LIBI386SRCS= boot.c biosdisk.c bootinfo.c bootinfo_biosgeom.c
 LIBI386SRCS+= bootmenu.c diskbuf.c exec.c menuutils.c
 LIBI386SRCS+= panic.c parseutils.c pread.c
-LIBI386SRCS+= eficons.c efidelay.c efidev.c efidisk.c efidisk_ll.c
+LIBI386SRCS+= efichar.c eficons.c efidelay.c efidev.c efidisk.c efidisk_ll.c
 LIBI386SRCS+= efigetsecs.c efimemory.c
 SRCS= ${SOURCES} ${EXTRA_SOURCES} ${LIBI386SRCS}
 

Index: src/sys/arch/i386/stand/efiboot/boot.c
diff -u src/sys/arch/i386/stand/efiboot/boot.c:1.5.2.2 src/sys/arch/i386/stand/efiboot/boot.c:1.5.2.3
--- src/sys/arch/i386/stand/efiboot/boot.c:1.5.2.2	Wed Mar 21 10:50:49 2018
+++ src/sys/arch/i386/stand/efiboot/boot.c	Mon Apr  2 08:50:33 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: boot.c,v 1.5.2.2 2018/03/21 10:50:49 martin Exp $	*/
+/*	$NetBSD: boot.c,v 1.5.2.3 2018/04/02 08:50:33 martin Exp $	*/
 
 /*-
  * Copyright (c) 2016 Kimihiro Nonaka <non...@netbsd.org>
@@ -463,11 +463,16 @@ command_consdev(char *arg)
 	char *sep, *sep2 = NULL;
 	int ioport, speed = 0;
 
+	if (*arg == '\0') {
+		efi_cons_show();
+		return;
+	}
+
 	sep = strchr(arg, ',');
 	if (sep != NULL) {
 		*sep++ = '\0';
 		sep2 = strchr(sep, ',');
-		if (sep != NULL)
+		if (sep2 != NULL)
 			*sep2++ = '\0';
 	}
 
@@ -555,6 +560,8 @@ void
 command_version(char *arg)
 {
 	CHAR16 *path;
+	char *upath, *ufirmware;
+	int rv;
 
 	if (strcmp(arg, "full") == 0) {
 		printf("ImageBase: 0x%" PRIxPTR "\n",
@@ -562,12 +569,24 @@ command_version(char *arg)
 		printf("Stack: 0x%" PRIxPTR "\n", efi_main_sp);
 		printf("EFI version: %d.%02d\n",
 		    ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
-		Print(L"EFI Firmware: %s (rev %d.%02d)\n", ST->FirmwareVendor,
-		    ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+		ufirmware = NULL;
+		rv = ucs2_to_utf8(ST->FirmwareVendor, &ufirmware);
+		if (rv == 0) {
+			printf("EFI Firmware: %s (rev %d.%02d)\n", ufirmware,
+			    ST->FirmwareRevision >> 16,
+			    ST->FirmwareRevision & 0xffff);
+			FreePool(ufirmware);
+		}
 		path = DevicePathToStr(efi_bootdp);
-		Print(L"Boot DevicePath: %d:%d:%s\n", DevicePathType(efi_bootdp),
-		    DevicePathSubType(efi_bootdp), path);
+		upath = NULL;
+		rv = ucs2_to_utf8(path, &upath);
 		FreePool(path);
+		if (rv == 0) {
+			printf("Boot DevicePath: %d:%d:%s\n",
+			    DevicePathType(efi_bootdp),
+			    DevicePathSubType(efi_bootdp), upath);
+			FreePool(upath);
+		}
 	}
 
 	printf("\n"
@@ -603,7 +622,9 @@ command_devpath(char *arg)
 	EFI_HANDLE *handles;
 	EFI_DEVICE_PATH *dp0, *dp;
 	CHAR16 *path;
+	char *upath;
 	UINTN cols, rows, row = 0;
+	int rv;
 
 	status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut,
 	    ST->ConOut->Mode->Mode, &cols, &rows);
@@ -626,41 +647,54 @@ command_devpath(char *arg)
 		if (EFI_ERROR(status))
 			break;
 
-		Print(L"DevicePathType %d\n", DevicePathType(dp0));
+		printf("DevicePathType %d\n", DevicePathType(dp0));
 		if (++row >= rows) {
 			row = 0;
-			Print(L"Press Any Key to continue :");
+			printf("Press Any Key to continue :");
 			(void) awaitkey(-1, 0);
-			Print(L"\n");
+			printf("\n");
 		}
 		for (dp = dp0;
 		     !IsDevicePathEnd(dp);
 		     dp = NextDevicePathNode(dp)) {
+
 			path = DevicePathToStr(dp);
-			Print(L"%d:%d:%s\n", DevicePathType(dp), DevicePathSubType(dp), path);
+			upath = NULL;
+			rv = ucs2_to_utf8(path, &upath);
 			FreePool(path);
+			if (rv) {
+				printf("convert failed\n");
+				break;
+			}
+
+			printf("%d:%d:%s\n", DevicePathType(dp),
+			    DevicePathSubType(dp), upath);
+			FreePool(upath);
 
 			if (++row >= rows) {
 				row = 0;
-				Print(L"Press Any Key to continue :");
+				printf("Press Any Key to continue :");
 				(void) awaitkey(-1, 0);
-				Print(L"\n");
+				printf("\n");
 			}
 		}
 	}
 }
 
+
 void
 command_efivar(char *arg)
 {
-	static const CHAR16 header[] =
-	 L"GUID                                Variable Name        Value\n"
-	 L"=================================== ==================== ========\n";
+	static const char header[] =
+	 "GUID                                 Variable Name        Value\n"
+	 "==================================== ==================== ========\n";
 	EFI_STATUS status;
 	UINTN sz = 64, osz;
-	CHAR16 *name = NULL, *tmp, *val;
+	CHAR16 *name = NULL, *tmp, *val, guid[128];
+	char *uname, *uval, *uguid;
 	EFI_GUID vendor;
 	UINTN cols, rows, row = 0;
+	int rv;
 
 	status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut,
 	    ST->ConOut->Mode->Mode, &cols, &rows);
@@ -671,15 +705,15 @@ command_efivar(char *arg)
 
 	name = AllocatePool(sz);
 	if (name == NULL) {
-		Print(L"memory allocation failed: %ld bytes\n",
-		    (UINT64)sz);
+		printf("memory allocation failed: %" PRIuMAX" bytes\n",
+		    (uintmax_t)sz);
 		return;
 	}
 
 	SetMem(name, sz, 0);
 	vendor = NullGuid;
 
-	Print(L"%s", header);
+	printf("%s", header);
 	for (;;) {
 		osz = sz;
 		status = uefi_call_wrapper(RT->GetNextVariableName, 3,
@@ -688,15 +722,15 @@ command_efivar(char *arg)
 			if (status == EFI_NOT_FOUND)
 				break;
 			if (status != EFI_BUFFER_TOO_SMALL) {
-				Print(L"GetNextVariableName failed: %r\n",
-				    status);
+				printf("GetNextVariableName failed: %" PRIxMAX "\n",
+				    (uintmax_t)status);
 				break;
 			}
 
 			tmp = AllocatePool(sz);
 			if (tmp == NULL) {
-				Print(L"memory allocation failed: %ld bytes\n",
-				    (UINT64)sz);
+				printf("memory allocation failed: %" PRIuMAX
+				    "bytes\n", (uintmax_t)sz);
 				break;
 			}
 			SetMem(tmp, sz, 0);
@@ -707,15 +741,43 @@ command_efivar(char *arg)
 		}
 
 		val = LibGetVariable(name, &vendor);
-		Print(L"%.-35g %.-20s %s\n", &vendor, name,
-		    val ? val : L"(null)");
-		FreePool(val);
+		if (val != NULL) {
+			uval = NULL;
+			rv = ucs2_to_utf8(val, &uval);
+			FreePool(val);
+			if (rv) {
+				printf("value convert failed\n");
+				break;
+			}
+		} else
+			uval = NULL;
+		uname = NULL;
+		rv = ucs2_to_utf8(name, &uname);
+		if (rv) {
+			printf("name convert failed\n");
+			FreePool(uval);
+			break;
+		}
+		GuidToString(guid, &vendor);
+		uguid = NULL;
+		rv = ucs2_to_utf8(guid, &uguid);
+		if (rv) {
+			printf("GUID convert failed\n");
+			FreePool(uval);
+			FreePool(uname);
+			break;
+		}
+		printf("%-35s %-20s %s\n", uguid, uname, uval ? uval : "(null)");
+		FreePool(uguid);
+		FreePool(uname);
+		if (uval != NULL)
+			FreePool(uval);
 
 		if (++row >= rows) {
 			row = 0;
-			Print(L"Press Any Key to continue :");
+			printf("Press Any Key to continue :");
 			(void) awaitkey(-1, 0);
-			Print(L"\n");
+			printf("\n");
 		}
 	}
 

Index: src/sys/arch/i386/stand/efiboot/efiboot.c
diff -u src/sys/arch/i386/stand/efiboot/efiboot.c:1.4.10.1 src/sys/arch/i386/stand/efiboot/efiboot.c:1.4.10.2
--- src/sys/arch/i386/stand/efiboot/efiboot.c:1.4.10.1	Tue Mar 13 14:54:52 2018
+++ src/sys/arch/i386/stand/efiboot/efiboot.c	Mon Apr  2 08:50:33 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: efiboot.c,v 1.4.10.1 2018/03/13 14:54:52 martin Exp $	*/
+/*	$NetBSD: efiboot.c,v 1.4.10.2 2018/04/02 08:50:33 martin Exp $	*/
 
 /*-
  * Copyright (c) 2016 Kimihiro Nonaka <non...@netbsd.org>
@@ -68,11 +68,13 @@ efi_main(EFI_HANDLE imageHandle, EFI_SYS
 	status = uefi_call_wrapper(BS->HandleProtocol, 3, IH,
 	    &LoadedImageProtocol, (void **)&efi_li);
 	if (EFI_ERROR(status))
-		Panic(L"HandleProtocol(LoadedImageProtocol): %r", status);
+		panic("HandleProtocol(LoadedImageProtocol): %" PRIxMAX,
+		    (uintmax_t)status);
 	status = uefi_call_wrapper(BS->HandleProtocol, 3, efi_li->DeviceHandle,
 	    &DevicePathProtocol, (void **)&dp0);
 	if (EFI_ERROR(status))
-		Panic(L"HandleProtocol(DevicePathProtocol): %r", status);
+		panic("HandleProtocol(DevicePathProtocol): %" PRIxMAX,
+		    (uintmax_t)status);
 	efi_bootdp = dp0;
 	for (dp = dp0; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp)) {
 		if (DevicePathType(dp) == MEDIA_DEVICE_PATH &&
@@ -86,7 +88,7 @@ efi_main(EFI_HANDLE imageHandle, EFI_SYS
 
 	status = uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0, 0, NULL);
 	if (EFI_ERROR(status))
-		Panic(L"SetWatchdogTimer: %r", status);
+		panic("SetWatchdogTimer: %" PRIxMAX, (uintmax_t)status);
 
 	boot();
 
@@ -122,7 +124,7 @@ efi_cleanup(void)
 		    &DescriptorVersion, true);
 		status = uefi_call_wrapper(BS->ExitBootServices, 2, IH, MapKey);
 		if (EFI_ERROR(status))
-			Panic(L"ExitBootServices failed");
+			panic("ExitBootServices failed");
 	}
 	efi_cleanuped = true;
 

Index: src/sys/arch/i386/stand/efiboot/efiboot.h
diff -u src/sys/arch/i386/stand/efiboot/efiboot.h:1.5.2.1 src/sys/arch/i386/stand/efiboot/efiboot.h:1.5.2.2
--- src/sys/arch/i386/stand/efiboot/efiboot.h:1.5.2.1	Tue Mar 13 14:54:52 2018
+++ src/sys/arch/i386/stand/efiboot/efiboot.h	Mon Apr  2 08:50:33 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: efiboot.h,v 1.5.2.1 2018/03/13 14:54:52 martin Exp $	*/
+/*	$NetBSD: efiboot.h,v 1.5.2.2 2018/04/02 08:50:33 martin Exp $	*/
 
 /*-
  * Copyright (c) 2016 Kimihiro Nonaka <non...@netbsd.org>
@@ -54,9 +54,15 @@ extern u_long efi_kernel_size;
 extern bool efi_cleanuped;
 void efi_cleanup(void);
 
+/* efichar.c */
+size_t ucs2len(const CHAR16 *);
+int ucs2_to_utf8(const CHAR16 *, char **);
+int utf8_to_ucs2(const char *, CHAR16 **, size_t *);
+
 /* eficons.c */
 int cninit(void);
 void consinit(int, int, int);
+void efi_cons_show(void);
 void command_text(char *);
 void command_gop(char *);
 

Index: src/sys/arch/i386/stand/efiboot/eficons.c
diff -u src/sys/arch/i386/stand/efiboot/eficons.c:1.4 src/sys/arch/i386/stand/efiboot/eficons.c:1.4.2.1
--- src/sys/arch/i386/stand/efiboot/eficons.c:1.4	Mon May  1 13:03:01 2017
+++ src/sys/arch/i386/stand/efiboot/eficons.c	Mon Apr  2 08:50:33 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: eficons.c,v 1.4 2017/05/01 13:03:01 nonaka Exp $	*/
+/*	$NetBSD: eficons.c,v 1.4.2.1 2018/04/02 08:50:33 martin Exp $	*/
 
 /*-
  * Copyright (c) 2016 Kimihiro Nonaka <non...@netbsd.org>
@@ -40,14 +40,43 @@ struct btinfo_console btinfo_console;
 
 static EFI_GRAPHICS_OUTPUT_PROTOCOL *efi_gop;
 static int efi_gop_mode = -1;
-
 static CHAR16 keybuf[16];
 static int keybuf_read = 0;
 static int keybuf_write = 0;
 
+static SERIAL_IO_INTERFACE *serios[4];
+static int default_comspeed =
+#if defined(CONSPEED)
+    CONSPEED;
+#else
+    9600;
+#endif
+static u_char serbuf[16];
+static int serbuf_read = 0;
+static int serbuf_write = 0;
+
 static void eficons_init_video(void);
 static void efi_switch_video_to_text_mode(void);
 
+static int efi_cons_getc(void);
+static int efi_cons_putc(int);
+static int efi_cons_iskey(int);
+static int efi_cons_waitforinputevent(uint64_t);
+
+static void efi_com_probe(void);
+static bool efi_valid_com(int);
+static int efi_com_init(int, int);
+static int efi_com_getc(void);
+static int efi_com_putc(int);
+static int efi_com_status(int);
+static int efi_com_waitforinputevent(uint64_t);
+
+static int iodev;
+static int (*internal_getchar)(void) = efi_cons_getc;
+static int (*internal_putchar)(int) = efi_cons_putc;
+static int (*internal_iskey)(int) = efi_cons_iskey;
+static int (*internal_waitforinputevent)(uint64_t) = efi_cons_waitforinputevent;
+
 static int
 getcomaddr(int idx)
 {
@@ -64,33 +93,48 @@ getcomaddr(int idx)
 void
 consinit(int dev, int ioport, int speed)
 {
-	int iodev;
+	int i;
 
-#if defined(CONSPEED)
-	btinfo_console.speed = CONSPEED;
-#else
-	btinfo_console.speed = 9600;
-#endif
+	btinfo_console.speed = default_comspeed;
 
 	switch (dev) {
 	case CONSDEV_AUTO:
-		/* XXX comport */
+		for (i = 0; i < __arraycount(serios); i++) {
+			iodev = CONSDEV_COM0 + i;
+			if (!efi_valid_com(iodev))
+				continue;
+			btinfo_console.addr = getcomaddr(i);
+
+			efi_cons_putc('0' + i);
+			efi_com_init(btinfo_console.addr, btinfo_console.speed);
+			/* check for:
+			 *  1. successful output
+			 *  2. optionally, keypress within 7s
+			 */
+			if (efi_com_putc(':') &&
+			    efi_com_putc('-') &&
+			    efi_com_putc('(') &&
+			    awaitkey(7, 0))
+				goto ok;
+		}
 		goto nocom;
+ok:
+		break;
 
 	case CONSDEV_COM0:
 	case CONSDEV_COM1:
 	case CONSDEV_COM2:
 	case CONSDEV_COM3:
 		iodev = dev;
-comport:
 		btinfo_console.addr = ioport;
 		if (btinfo_console.addr == 0) {
-			btinfo_console.addr = getcomaddr(iodev - CONSDEV_COM0);
-			if (btinfo_console.addr == 0)
+			if (!efi_valid_com(iodev))
 				goto nocom;
+			btinfo_console.addr = getcomaddr(iodev - CONSDEV_COM0);
 		}
 		if (speed != 0)
 			btinfo_console.speed = speed;
+		efi_com_init(btinfo_console.addr, btinfo_console.speed);
 		break;
 
 	case CONSDEV_COM0KBD:
@@ -98,12 +142,33 @@ comport:
 	case CONSDEV_COM2KBD:
 	case CONSDEV_COM3KBD:
 		iodev = dev - CONSDEV_COM0KBD + CONSDEV_COM0;
-		goto comport;	/* XXX kbd */
-
+		if (!efi_valid_com(iodev))
+			goto nocom;
+		btinfo_console.addr = getcomaddr(iodev - CONSDEV_COM0);
+
+		efi_cons_putc('0' + iodev - CONSDEV_COM0);
+		efi_com_init(btinfo_console.addr, btinfo_console.speed);
+		/* check for:
+		 *  1. successful output
+		 *  2. optionally, keypress within 7s
+		 */
+		if (efi_com_putc(':') &&
+		    efi_com_putc('-') &&
+		    efi_com_putc('(') &&
+		    awaitkey(7, 0))
+			goto kbd;
+		/*FALLTHROUGH*/
 	case CONSDEV_PC:
 	default:
 nocom:
 		iodev = CONSDEV_PC;
+		internal_putchar = efi_cons_putc;
+kbd:
+		internal_getchar = efi_cons_getc;
+		internal_iskey = efi_cons_iskey;
+		internal_waitforinputevent = efi_cons_waitforinputevent;
+		memset(keybuf, 0, sizeof(keybuf));
+		keybuf_read = keybuf_write = 0;
 		break;
 	}
 
@@ -116,6 +181,7 @@ cninit(void)
 
 	efi_switch_video_to_text_mode();
 	eficons_init_video();
+	efi_com_probe();
 
 	consinit(boot_params.bp_consdev, boot_params.bp_consaddr,
 	    boot_params.bp_conspeed);
@@ -123,8 +189,50 @@ cninit(void)
 	return 0;
 }
 
-int
-getchar(void)
+void
+efi_cons_show(void)
+{
+	const bool pc_is_console = strcmp(btinfo_console.devname, "pc") == 0;
+	const bool com_is_console = strcmp(btinfo_console.devname, "com") == 0;
+	bool first = true;
+	bool found = false;
+	int i;
+
+	if (efi_gop != NULL) {
+		printf("pc");
+		if (pc_is_console)
+			printf("*");
+		first = false;
+	}
+
+	for (i = 0; i < __arraycount(serios); i++) {
+		if (serios[i] != NULL) {
+			if (!first)
+				printf(" ");
+			first = false;
+
+			printf("com%d", i);
+			if (com_is_console &&
+			    btinfo_console.addr == getcomaddr(i)) {
+				printf(",%d*", btinfo_console.speed);
+				found = true;
+			}
+		}
+	}
+	if (!found && com_is_console) {
+		if (!first)
+			printf(" ");
+		first = false;
+
+		printf("com,0x%x,%d*", btinfo_console.addr,
+		    btinfo_console.speed);
+	}
+
+	printf("\n");
+}
+
+static int
+efi_cons_getc(void)
 {
 	EFI_STATUS status;
 	EFI_INPUT_KEY key;
@@ -146,23 +254,21 @@ getchar(void)
 	return key.UnicodeChar;
 }
 
-void
-putchar(int c)
+static int
+efi_cons_putc(int c)
 {
 	CHAR16 buf[2];
 
-	buf[1] = 0;
-	if (c == '\n') {
-		buf[0] = '\r';
-		Output(buf);
-	}
 	buf[0] = c;
+	buf[1] = 0;
 	Output(buf);
+
+	return 1;
 }
 
 /*ARGSUSED*/
-int
-iskey(int intr)
+static int
+efi_cons_iskey(int intr)
 {
 	EFI_STATUS status;
 	EFI_INPUT_KEY key;
@@ -180,13 +286,49 @@ iskey(int intr)
 	return 1;
 }
 
+static int
+efi_cons_waitforinputevent(uint64_t timeout)
+{
+	EFI_STATUS status;
+
+	status = WaitForSingleEvent(ST->ConIn->WaitForKey, timeout);
+	if (!EFI_ERROR(status))
+		return 0;
+	if (status == EFI_TIMEOUT)
+		return ETIMEDOUT;
+	return EINVAL;
+}
+
+int
+getchar(void)
+{
+
+	return internal_getchar();
+}
+
+void
+putchar(int c)
+{
+
+	if (c == '\n')
+		internal_putchar('\r');
+	internal_putchar(c);
+}
+
+int
+iskey(int intr)
+{
+
+	return internal_iskey(intr);
+}
+
 char
 awaitkey(int timeout, int tell)
 {
 	char c = 0;
 
 	for (;;) {
-		if (tell) {
+		if (tell && timeout) {
 			char numbuf[32];
 			int len;
 
@@ -210,7 +352,7 @@ awaitkey(int timeout, int tell)
 			goto out;
 		}
 		if (timeout--)
-			WaitForSingleEvent(ST->ConIn->WaitForKey, 10000000);
+			internal_waitforinputevent(10000000);
 		else
 			break;
 	}
@@ -307,7 +449,8 @@ bi_framebuffer(void)
 		status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop,
 		    bestmode);
 		if (EFI_ERROR(status) || efi_gop->Mode->Mode != bestmode)
-			Print(L"GOP setmode failed: %r\n", status);
+			printf("GOP setmode failed: %" PRIxMAX "\n",
+			    (uintmax_t)status);
 	}
 
 	info = efi_gop->Mode->Info;
@@ -350,7 +493,7 @@ bi_framebuffer(void)
 
 	case PixelBltOnly:
 	case PixelFormatMax:
-		Panic(L"Error: invalid pixel format (%d)", info->PixelFormat);
+		panic("Error: invalid pixel format (%d)", info->PixelFormat);
 		break;
 	}
 
@@ -378,8 +521,8 @@ print_text_modes(void)
 		    ST->ConOut, i, &cols, &rows);
 		if (EFI_ERROR(status))
 			continue;
-		Print(L"%c%d: %dx%d\n", i == curmode ? '*' : ' ',
-		    i, cols, rows);
+		printf("%c%d: %" PRIxMAX "x%" PRIxMAX "\n",
+		    i == curmode ? '*' : ' ', i, (uintmax_t)cols, (uintmax_t)rows);
 	}
 }
 
@@ -396,7 +539,8 @@ efi_find_text_mode(char *arg)
 		    ST->ConOut, i, &cols, &rows);
 		if (EFI_ERROR(status))
 			continue;
-		snprintf(mode, sizeof(mode), "%lux%lu", (long)cols, (long)rows);
+		snprintf(mode, sizeof(mode), "%" PRIuMAX "x%" PRIuMAX,
+		    (uintmax_t)cols, (uintmax_t)rows);
 		if (strcmp(arg, mode) == 0)
 			return i;
 	}
@@ -456,36 +600,36 @@ print_gop_modes(void)
 		if (EFI_ERROR(status))
 			continue;
 
-		Print(L"%c%d: %dx%d ",
+		printf("%c%d: %dx%d ",
 		    memcmp(info, efi_gop->Mode->Info, sizeof(*info)) == 0 ?
 		      '*' : ' ',
 		      i, info->HorizontalResolution, info->VerticalResolution);
 		switch (info->PixelFormat) {
 		case PixelRedGreenBlueReserved8BitPerColor:
-			Print(L"RGBR");
+			printf("RGBR");
 			break;
 		case PixelBlueGreenRedReserved8BitPerColor:
-			Print(L"BGRR");
+			printf("BGRR");
 			break;
 		case PixelBitMask:
-			Print(L"R:%08x G:%08x B:%08x X:%08x",
+			printf("R:%08x G:%08x B:%08x X:%08x",
 			    info->PixelInformation.RedMask,
 			    info->PixelInformation.GreenMask,
 			    info->PixelInformation.BlueMask,
 			    info->PixelInformation.ReservedMask);
 			break;
 		case PixelBltOnly:
-			Print(L"(blt only)");
+			printf("(blt only)");
 			break;
 		default:
-			Print(L"(Invalid pixel format)");
+			printf("(Invalid pixel format)");
 			break;
 		}
-		Print(L" pitch %d", info->PixelsPerScanLine);
+		printf(" pitch %d", info->PixelsPerScanLine);
 		depth = getdepth(info);
 		if (depth > 0)
-			Print(L" bpp %d", depth);
-		Print(L"\n");
+			printf(" bpp %d", depth);
+		printf("\n");
 	}
 
 	return 0;
@@ -623,3 +767,246 @@ efi_switch_video_to_text_mode(void)
 		    EfiConsoleControlScreenText);
 	}
 }
+
+/*
+ * serial port
+ */
+static void
+efi_com_probe(void)
+{
+	EFI_STATUS status;
+	UINTN i, nhandles;
+	EFI_HANDLE *handles;
+	EFI_DEVICE_PATH	*dp, *dp0;
+	EFI_DEV_PATH_PTR dpp;
+	SERIAL_IO_INTERFACE *serio;
+	int uid = -1;
+
+	status = LibLocateHandle(ByProtocol, &SerialIoProtocol, NULL,
+	    &nhandles, &handles);
+	if (EFI_ERROR(status))
+		return;
+
+	for (i = 0; i < nhandles; i++) {
+		/*
+		 * Identify port number of the handle.  This assumes ACPI
+		 * UID 0-3 map to legacy COM[1-4] and they use the legacy
+		 * port address.
+		 */
+		status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i],
+		    &DevicePathProtocol, (void **)&dp0);
+		if (EFI_ERROR(status))
+			continue;
+
+		for (uid = -1, dp = dp0;
+		     !IsDevicePathEnd(dp);
+		     dp = NextDevicePathNode(dp)) {
+
+			if (DevicePathType(dp) == ACPI_DEVICE_PATH &&
+			    DevicePathSubType(dp) == ACPI_DP) {
+				dpp = (EFI_DEV_PATH_PTR)dp;
+				if (dpp.Acpi->HID == EISA_PNP_ID(0x0501)) {
+					uid = dpp.Acpi->UID;
+					break;
+				}
+			}
+		}
+		if (uid < 0 || __arraycount(serios) <= uid)
+			continue;
+
+		/* Prepare SERIAL_IO_INTERFACE */
+		status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i],
+		    &SerialIoProtocol, (void **)&serio);
+		if (EFI_ERROR(status))
+			continue;
+
+		serios[uid] = serio;
+	}
+
+	FreePool(handles);
+
+}
+
+static bool
+efi_valid_com(int dev)
+{
+	int idx;
+
+	switch (dev) {
+	default:
+	case CONSDEV_PC:
+		return false;
+
+	case CONSDEV_COM0:
+	case CONSDEV_COM1:
+	case CONSDEV_COM2:
+	case CONSDEV_COM3:
+		idx = dev - CONSDEV_COM0;
+		break;
+	}
+
+	return idx < __arraycount(serios) &&
+	    serios[idx] != NULL &&
+	    getcomaddr(idx) != 0;
+}
+
+static int
+efi_com_init(int addr, int speed)
+{
+	EFI_STATUS status;
+	SERIAL_IO_INTERFACE *serio;
+
+	if (speed <= 0)
+		return 0;
+
+	if (!efi_valid_com(iodev))
+		return 0;
+
+	serio = serios[iodev - CONSDEV_COM0];
+
+	if (serio->Mode->BaudRate != btinfo_console.speed) {
+		status = uefi_call_wrapper(serio->SetAttributes, 7, serio,
+		    speed, serio->Mode->ReceiveFifoDepth,
+		    serio->Mode->Timeout, serio->Mode->Parity,
+		    serio->Mode->DataBits, serio->Mode->StopBits);
+		if (EFI_ERROR(status)) {
+			printf("com%d: SetAttribute() failed with status=%" PRIxMAX
+			    "\n", iodev - CONSDEV_COM0, (uintmax_t)status);
+			return 0;
+		}
+	}
+
+	default_comspeed = speed;
+	internal_getchar = efi_com_getc;
+	internal_putchar = efi_com_putc;
+	internal_iskey = efi_com_status;
+	internal_waitforinputevent = efi_com_waitforinputevent;
+	memset(serbuf, 0, sizeof(serbuf));
+	serbuf_read = serbuf_write = 0;
+
+	return speed;
+}
+
+static int
+efi_com_getc(void)
+{
+	EFI_STATUS status;
+	SERIAL_IO_INTERFACE *serio;
+	UINTN sz;
+	u_char c;
+
+	if (!efi_valid_com(iodev))
+		panic("Invalid serial port: iodev=%d", iodev);
+
+	if (serbuf_read != serbuf_write) {
+		c = serbuf[serbuf_read];
+		serbuf_read = (serbuf_read + 1) % __arraycount(serbuf);
+		return c;
+	}
+
+	serio = serios[iodev - CONSDEV_COM0];
+
+	for (;;) {
+		sz = 1;
+		status = uefi_call_wrapper(serio->Read, 3, serio, &sz, &c);
+		if (!EFI_ERROR(status) && sz > 0)
+			break;
+		if (status != EFI_TIMEOUT && EFI_ERROR(status))
+			panic("Error reading from serial status=%"PRIxMAX,
+			    (uintmax_t)status);
+	}
+	return c;
+}
+
+static int
+efi_com_putc(int c)
+{
+	EFI_STATUS status;
+	SERIAL_IO_INTERFACE *serio;
+	UINTN sz = 1;
+	u_char buf;
+
+	if (!efi_valid_com(iodev))
+		return 0;
+
+	serio = serios[iodev - CONSDEV_COM0];
+	buf = c;
+	status = uefi_call_wrapper(serio->Write, 3, serio, &sz, &buf);
+	if (EFI_ERROR(status) || sz < 1)
+		return 0;
+	return 1;
+}
+
+/*ARGSUSED*/
+static int
+efi_com_status(int intr)
+{
+	EFI_STATUS status;
+	SERIAL_IO_INTERFACE *serio;
+	UINTN sz;
+	u_char c;
+
+	if (!efi_valid_com(iodev))
+		panic("Invalid serial port: iodev=%d", iodev);
+
+	if (serbuf_read != serbuf_write)
+		return 1;
+
+	serio = serios[iodev - CONSDEV_COM0];
+	sz = 1;
+	status = uefi_call_wrapper(serio->Read, 3, serio, &sz, &c);
+	if (EFI_ERROR(status) || sz < 1)
+		return 0;
+
+	serbuf[serbuf_write] = c;
+	serbuf_write = (serbuf_write + 1) % __arraycount(serbuf);
+	return 1;
+}
+
+static void
+efi_com_periodic_event(EFI_EVENT event, void *ctx)
+{
+	EFI_EVENT timer = ctx;
+
+	if (efi_com_status(0)) {
+		uefi_call_wrapper(BS->SetTimer, 3, event, TimerCancel, 0);
+		uefi_call_wrapper(BS->SignalEvent, 1, timer);
+	}
+}
+
+static int
+efi_com_waitforinputevent(uint64_t timeout)
+{
+	EFI_STATUS status;
+	EFI_EVENT timer, periodic;
+
+	status = uefi_call_wrapper(BS->CreateEvent, 5, EVT_TIMER, 0, NULL, NULL,
+	    &timer);
+	if (EFI_ERROR(status))
+		return EINVAL;
+
+        status = uefi_call_wrapper(BS->CreateEvent, 5,
+	    EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, efi_com_periodic_event,
+	    timer, &periodic);
+	if (EFI_ERROR(status)) {
+		uefi_call_wrapper(BS->CloseEvent, 1, timer);
+		return EINVAL;
+	}
+
+	status = uefi_call_wrapper(BS->SetTimer, 3, periodic, TimerPeriodic,
+	    1000000);	/* 100ms */
+	if (EFI_ERROR(status)) {
+		uefi_call_wrapper(BS->CloseEvent, 1, periodic);
+		uefi_call_wrapper(BS->CloseEvent, 1, timer);
+		return EINVAL;
+	}
+	status = WaitForSingleEvent(&timer, timeout);
+	uefi_call_wrapper(BS->SetTimer, 3, periodic, TimerCancel, 0);
+	uefi_call_wrapper(BS->CloseEvent, 1, periodic);
+	uefi_call_wrapper(BS->CloseEvent, 1, timer);
+	if (!EFI_ERROR(status))
+		return 0;
+	if (status == EFI_TIMEOUT)
+		return ETIMEDOUT;
+	return EINVAL;
+}

Index: src/sys/arch/i386/stand/efiboot/efidelay.c
diff -u src/sys/arch/i386/stand/efiboot/efidelay.c:1.1 src/sys/arch/i386/stand/efiboot/efidelay.c:1.1.12.1
--- src/sys/arch/i386/stand/efiboot/efidelay.c:1.1	Tue Jan 24 11:09:14 2017
+++ src/sys/arch/i386/stand/efiboot/efidelay.c	Mon Apr  2 08:50:33 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: efidelay.c,v 1.1 2017/01/24 11:09:14 nonaka Exp $	*/
+/*	$NetBSD: efidelay.c,v 1.1.12.1 2018/04/02 08:50:33 martin Exp $	*/
 
 /*-
  * Copyright (c) 2016 Kimihiro Nonaka <non...@netbsd.org>
@@ -32,10 +32,22 @@ void
 delay(int us)
 {
 	EFI_STATUS status;
+	CHAR16 errmsg[128];
+	char *uerrmsg;
+	int rv;
 
 	status = uefi_call_wrapper(BS->Stall, 1, us);
-	if (EFI_ERROR(status))
-		Panic(L"%a: couldn't delay %d us: %r\n", __func__, us, status);
+	if (EFI_ERROR(status)) {
+		StatusToString(errmsg, status);
+		uerrmsg = NULL;
+		rv = ucs2_to_utf8(errmsg, &uerrmsg);
+		if (rv)
+			uerrmsg = "";
+		panic("couldn't delay %d us: %s(%" PRIxMAX ")\n", us, uerrmsg,
+		    (uintmax_t)status);
+		if (rv == 0)
+			FreePool(uerrmsg);
+	}
 }
 
 void

Index: src/sys/arch/i386/stand/efiboot/efidisk.c
diff -u src/sys/arch/i386/stand/efiboot/efidisk.c:1.1.12.2 src/sys/arch/i386/stand/efiboot/efidisk.c:1.1.12.3
--- src/sys/arch/i386/stand/efiboot/efidisk.c:1.1.12.2	Wed Mar 21 10:50:49 2018
+++ src/sys/arch/i386/stand/efiboot/efidisk.c	Mon Apr  2 08:50:33 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: efidisk.c,v 1.1.12.2 2018/03/21 10:50:49 martin Exp $	*/
+/*	$NetBSD: efidisk.c,v 1.1.12.3 2018/04/02 08:50:33 martin Exp $	*/
 
 /*-
  * Copyright (c) 2016 Kimihiro Nonaka <non...@netbsd.org>
@@ -57,7 +57,8 @@ efi_disk_probe(void)
 	status = LibLocateHandle(ByProtocol, &BlockIoProtocol, NULL,
 	    &nhandles, &handles);
 	if (EFI_ERROR(status))
-		Panic(L"LocateHandle(BlockIoProtocol): %r", status);
+		panic("LocateHandle(BlockIoProtocol): %" PRIxMAX,
+		    (uintmax_t)status);
 
 	if (efi_bootdp != NULL)
 		depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH);
@@ -75,7 +76,8 @@ efi_disk_probe(void)
 		status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i],
 		    &BlockIoProtocol, (void **)&bio);
 		if (EFI_ERROR(status))
-			Panic(L"HandleProtocol(BlockIoProtocol): %r", status);
+			panic("HandleProtocol(BlockIoProtocol): %" PRIxMAX,
+			    (uintmax_t)status);
 
 		media = bio->Media;
 		if (media->LogicalPartition || !media->MediaPresent)

Index: src/sys/arch/i386/stand/efiboot/efimemory.c
diff -u src/sys/arch/i386/stand/efiboot/efimemory.c:1.4 src/sys/arch/i386/stand/efiboot/efimemory.c:1.4.10.1
--- src/sys/arch/i386/stand/efiboot/efimemory.c:1.4	Tue Feb 14 13:29:09 2017
+++ src/sys/arch/i386/stand/efiboot/efimemory.c	Mon Apr  2 08:50:33 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: efimemory.c,v 1.4 2017/02/14 13:29:09 nonaka Exp $	*/
+/*	$NetBSD: efimemory.c,v 1.4.10.1 2018/04/02 08:50:33 martin Exp $	*/
 
 /*-
  * Copyright (c) 2016 Kimihiro Nonaka <non...@netbsd.org>
@@ -110,7 +110,7 @@ efi_memory_get_map(UINTN *NoEntries, UIN
 	desc = LibMemoryMap(NoEntries, MapKey, DescriptorSize,
 	    DescriptorVersion);
 	if (desc == NULL)
-		Panic(L"efi_memory_get_map failed");
+		panic("efi_memory_get_map failed");
 
 	if (!sorted)
 		return desc;
@@ -238,7 +238,7 @@ efi_memory_probe(void)
 	mdtop = efi_memory_get_map(&NoEntries, &MapKey, &DescriptorSize,
 	    &DescriptorVersion, false);
 
-	Print(L" mem[");
+	printf(" mem[");
 	for (i = 0, n = 0, md = mdtop; i < NoEntries; i++, md = next) {
 		next = NextMemoryDescriptor(md, DescriptorSize);
 
@@ -251,11 +251,11 @@ efi_memory_probe(void)
 			continue;
 
 		if (n++ > 0)
-			Print(L" ");
-		Print(L"0x%lx-0x%lx", md->PhysicalStart,
-		    md->PhysicalStart + MappingSize - 1);
+			printf(" ");
+		printf("0x%" PRIxMAX "-0x%" PRIxMAX, (uintmax_t)md->PhysicalStart,
+		    (uintmax_t)(md->PhysicalStart + MappingSize - 1));
 	}
-	Print(L"]");
+	printf("]\n");
 
 	FreePool(mdtop);
 }
@@ -302,9 +302,9 @@ efi_memory_show_map(bool sorted)
 
 		if (++row >= rows) {
 			row = 0;
-			Print(L"Press Any Key to continue :");
+			printf("Press Any Key to continue :");
 			(void) awaitkey(-1, 0);
-			Print(L"\n");
+			printf("\n");
 		}
 	}
 

Index: src/sys/arch/i386/stand/efiboot/bootia32/efibootia32.c
diff -u src/sys/arch/i386/stand/efiboot/bootia32/efibootia32.c:1.3 src/sys/arch/i386/stand/efiboot/bootia32/efibootia32.c:1.3.2.1
--- src/sys/arch/i386/stand/efiboot/bootia32/efibootia32.c:1.3	Sat Apr 29 00:05:35 2017
+++ src/sys/arch/i386/stand/efiboot/bootia32/efibootia32.c	Mon Apr  2 08:50:33 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: efibootia32.c,v 1.3 2017/04/29 00:05:35 nonaka Exp $	*/
+/*	$NetBSD: efibootia32.c,v 1.3.2.1 2018/04/02 08:50:33 martin Exp $	*/
 
 /*-
  * Copyright (c) 2016 Kimihiro Nonaka <non...@netbsd.org>
@@ -46,8 +46,8 @@ efi_md_init(void)
 	status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress,
 	    EfiLoaderData, sz, &addr);
 	if (EFI_ERROR(status))
-		Panic(L"%a: AllocatePages() failed: %d page(s): %r",
-		    __func__, sz, status);
+		panic("%s: AllocatePages() failed: %d page(s): %" PRIxMAX,
+		    __func__, sz, (uintmax_t)status);
 	startprog32 = (void *)(u_long)addr;
 	CopyMem(startprog32, startprog32_start, startprog32_size);
 }
@@ -67,5 +67,5 @@ startprog(physaddr_t entry, uint32_t arg
 void
 multiboot(physaddr_t entry, physaddr_t header, physaddr_t sp)
 {
-	Panic(L"%a: not implemented", __func__);
+	panic("%s: not implemented", __func__);
 }

Index: src/sys/arch/i386/stand/efiboot/bootx64/efibootx64.c
diff -u src/sys/arch/i386/stand/efiboot/bootx64/efibootx64.c:1.3 src/sys/arch/i386/stand/efiboot/bootx64/efibootx64.c:1.3.2.1
--- src/sys/arch/i386/stand/efiboot/bootx64/efibootx64.c:1.3	Sat Apr 29 00:05:35 2017
+++ src/sys/arch/i386/stand/efiboot/bootx64/efibootx64.c	Mon Apr  2 08:50:33 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: efibootx64.c,v 1.3 2017/04/29 00:05:35 nonaka Exp $	*/
+/*	$NetBSD: efibootx64.c,v 1.3.2.1 2018/04/02 08:50:33 martin Exp $	*/
 
 /*-
  * Copyright (c) 2016 Kimihiro Nonaka <non...@netbsd.org>
@@ -46,8 +46,8 @@ efi_md_init(void)
 	status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress,
 	    EfiLoaderData, sz, &addr);
 	if (EFI_ERROR(status))
-		Panic(L"%a: AllocatePages() failed: %d page(s): %r",
-		    __func__, sz, status);
+		panic("%s: AllocatePages() failed: %d page(s): %" PRIxMAX,
+		    __func__, sz, (uintmax_t)status);
 	startprog64 = (void *)addr;
 	CopyMem(startprog64, startprog64_start, startprog64_size);
 }
@@ -72,5 +72,5 @@ startprog(physaddr_t entry, uint32_t arg
 void
 multiboot(physaddr_t entry, physaddr_t header, physaddr_t sp)
 {
-	Panic(L"%a: not implemented", __func__);
+	panic("%s: not implemented", __func__);
 }

Added files:

Index: src/sys/arch/i386/stand/efiboot/efichar.c
diff -u /dev/null src/sys/arch/i386/stand/efiboot/efichar.c:1.1.4.2
--- /dev/null	Mon Apr  2 08:50:33 2018
+++ src/sys/arch/i386/stand/efiboot/efichar.c	Mon Apr  2 08:50:33 2018
@@ -0,0 +1,195 @@
+/*	$NetBSD: efichar.c,v 1.1.4.2 2018/04/02 08:50:33 martin Exp $	*/
+
+/*-
+ * Copyright (c) 2010 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if 0
+__FBSDID("$FreeBSD: head/stand/efi/libefi/efichar.c 328061 2018-01-16 20:35:54Z tsoome $");
+#endif
+
+#include "efiboot.h"
+
+size_t
+ucs2len(const CHAR16 *str)
+{
+	size_t i;
+
+	i = 0;
+	while (*str++)
+		i++;
+	return i;
+}
+
+/*
+ * If nm were converted to utf8, what what would strlen
+ * return on the resulting string?
+ */
+static size_t
+utf8_len_of_ucs2(const CHAR16 *nm)
+{
+	size_t len;
+	CHAR16 c;
+
+	len = 0;
+	while (*nm) {
+		c = *nm++;
+		if (c > 0x7ff)
+			len += 3;
+		else if (c > 0x7f)
+			len += 2;
+		else
+			len++;
+	}
+
+	return len;
+}
+
+int
+ucs2_to_utf8(const CHAR16 *nm, char **name)
+{
+	size_t len, sz;
+	CHAR16 c;
+	char *cp;
+	int freeit = *name == NULL;
+
+	sz = utf8_len_of_ucs2(nm) + 1;
+	len = 0;
+	if (*name != NULL)
+		cp = *name;
+	else
+		cp = *name = AllocatePool(sz);
+	if (*name == NULL)
+		return ENOMEM;
+
+	while (*nm) {
+		c = *nm++;
+		if (c > 0x7ff) {
+			if (len++ < sz)
+				*cp++ = (char)(0xE0 | (c >> 12));
+			if (len++ < sz)
+				*cp++ = (char)(0x80 | ((c >> 6) & 0x3f));
+			if (len++ < sz)
+				*cp++ = (char)(0x80 | (c & 0x3f));
+		} else if (c > 0x7f) {
+			if (len++ < sz)
+				*cp++ = (char)(0xC0 | ((c >> 6) & 0x1f));
+			if (len++ < sz)
+				*cp++ = (char)(0x80 | (c & 0x3f));
+		} else {
+			if (len++ < sz)
+				*cp++ = (char)(c & 0x7f);
+		}
+	}
+
+	if (len >= sz) {
+		/* Absent bugs, we'll never return EOVERFLOW */
+		if (freeit) {
+			FreePool(*name);
+			*name = NULL;
+		}
+		return EOVERFLOW;
+	}
+	*cp++ = '\0';
+
+	return 0;
+}
+
+int
+utf8_to_ucs2(const char *name, CHAR16 **nmp, size_t *len)
+{
+	CHAR16 *nm;
+	size_t sz;
+	uint32_t ucs4;
+	int c, bytes;
+	int freeit = *nmp == NULL;
+
+	sz = strlen(name) * 2 + 2;
+	if (*nmp == NULL)
+		*nmp = AllocatePool(sz);
+	if (*nmp == NULL)
+		return ENOMEM;
+	nm = *nmp;
+	*len = sz;
+
+	ucs4 = 0;
+	bytes = 0;
+	while (sz > 1 && *name != '\0') {
+		c = *name++;
+		/*
+		 * Conditionalize on the two major character types:
+		 * initial and followup characters.
+		 */
+		if ((c & 0xc0) != 0x80) {
+			/* Initial characters. */
+			if (bytes != 0)
+				goto ilseq;
+			if ((c & 0xf8) == 0xf0) {
+				ucs4 = c & 0x07;
+				bytes = 3;
+			} else if ((c & 0xf0) == 0xe0) {
+				ucs4 = c & 0x0f;
+				bytes = 2;
+			} else if ((c & 0xe0) == 0xc0) {
+				ucs4 = c & 0x1f;
+				bytes = 1;
+			} else {
+				ucs4 = c & 0x7f;
+				bytes = 0;
+			}
+		} else {
+			/* Followup characters. */
+			if (bytes > 0) {
+				ucs4 = (ucs4 << 6) + (c & 0x3f);
+				bytes--;
+			} else if (bytes == 0)
+				goto ilseq;
+		}
+		if (bytes == 0) {
+			if (ucs4 > 0xffff)
+				goto ilseq;
+			*nm++ = (CHAR16)ucs4;
+			sz -= 2;
+		}
+	}
+	if (sz < 2) {
+		if (freeit) {
+			FreePool(nm);
+			*nmp = NULL;
+		}
+		return EINVAL;
+	}
+	sz -= 2;
+	*nm = 0;
+	*len -= sz;
+	return 0;
+ilseq:
+	if (freeit) {
+		FreePool(nm);
+		*nmp = NULL;
+	}
+	return EILSEQ;
+}

Reply via email to