Add serial console support for efiboot.
authoryasuoka <yasuoka@openbsd.org>
Wed, 31 May 2017 08:40:32 +0000 (08:40 +0000)
committeryasuoka <yasuoka@openbsd.org>
Wed, 31 May 2017 08:40:32 +0000 (08:40 +0000)
sys/arch/amd64/stand/efiboot/conf.c
sys/arch/amd64/stand/efiboot/efiboot.c
sys/arch/amd64/stand/efiboot/efiboot.h
sys/arch/amd64/stand/libsa/dev_i386.c

index 0b2933d..c02719d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: conf.c,v 1.6 2016/09/18 16:36:09 jsing Exp $  */
+/*     $OpenBSD: conf.c,v 1.7 2017/05/31 08:40:32 yasuoka Exp $        */
 
 /*
  * Copyright (c) 1996 Michael Shalayeff
@@ -85,6 +85,7 @@ int ndevs = nitems(devsw);
 
 struct consdev constab[] = {
        { efi_cons_probe, efi_cons_init, efi_cons_getc, efi_cons_putc },
+       { efi_com_probe, efi_com_init, efi_com_getc, efi_com_putc },
        { NULL }
 };
 struct consdev *cn_tab = constab;
index d668258..25e34c1 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: efiboot.c,v 1.18 2017/05/16 02:53:28 yasuoka Exp $    */
+/*     $OpenBSD: efiboot.c,v 1.19 2017/05/31 08:40:32 yasuoka Exp $    */
 
 /*
  * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
@@ -387,6 +387,8 @@ static EFI_GUID                              con_guid
                                            = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
 static EFI_GUID                                 gop_guid
                                            = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+static EFI_GUID                                 serio_guid
+                                           = SERIAL_IO_PROTOCOL;
 struct efi_video {
        int     cols;
        int     rows;
@@ -501,10 +503,174 @@ efi_cons_getshifts(dev_t dev)
        return (0);
 }
 
-/* XXX: serial console is not supported yet */
 int com_addr = -1;
 int com_speed = -1;
 
+static SERIAL_IO_INTERFACE     *serios[4];
+
+void
+efi_com_probe(struct consdev *cn)
+{
+       EFI_HANDLE              *handles = NULL;
+       SERIAL_IO_INTERFACE     *serio;
+       EFI_STATUS               status;
+       EFI_DEVICE_PATH         *dp, *dp0;
+       EFI_DEV_PATH_PTR         dpp;
+       UINTN                    sz;
+       int                      i, uid = -1;
+
+       sz = 0;
+       status = EFI_CALL(BS->LocateHandle, ByProtocol, &serio_guid, 0, &sz, 0);
+       if (status == EFI_BUFFER_TOO_SMALL) {
+               handles = alloc(sz);
+               status = EFI_CALL(BS->LocateHandle, ByProtocol, &serio_guid,
+                   0, &sz, handles);
+       }
+       if (handles == NULL || EFI_ERROR(status))
+               panic("could not get handles of serial i/o");
+
+       for (i = 0; i < sz / sizeof(EFI_HANDLE); 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 = EFI_CALL(BS->HandleProtocol, handles[i], &devp_guid,
+                   (void **)&dp0);
+               if (EFI_ERROR(status))
+                       continue;
+               uid = -1;
+               for (dp = dp0; !IsDevicePathEnd(dp);
+                   dp = NextDevicePathNode(dp)) {
+                       dpp = (EFI_DEV_PATH_PTR)dp;
+                       if (DevicePathType(dp) == ACPI_DEVICE_PATH &&
+                           DevicePathSubType(dp) == ACPI_DP)
+                               if (dpp.Acpi->HID == EFI_PNP_ID(0x0501)) {
+                                       uid = dpp.Acpi->UID;
+                                       break;
+                               }
+               }
+               if (uid < 0 || nitems(serios) <= uid)
+                       continue;
+
+               /* Prepare SERIAL_IO_INTERFACE */
+               status = EFI_CALL(BS->HandleProtocol, handles[i], &serio_guid,
+                   (void **)&serio);
+               if (EFI_ERROR(status))
+                       continue;
+               serios[uid] = serio;
+       }
+       free(handles, sz);
+
+       for (i = 0; i < nitems(serios); i++) {
+               if (serios[i] != NULL)
+                       printf(" com%d", i);
+       }
+       cn->cn_pri = CN_LOWPRI;
+       cn->cn_dev = makedev(8, 0);
+}
+
+int
+efi_valid_com(dev_t dev)
+{
+       return (minor(dev) < nitems(serios) && serios[minor(dev)] != NULL);
+}
+
+int
+comspeed(dev_t dev, int sp)
+{
+       EFI_STATUS               status;
+       SERIAL_IO_INTERFACE     *serio = serios[minor(dev)];
+       int                      newsp;
+
+       if (sp <= 0)
+               return com_speed;
+
+       if (!efi_valid_com(dev))
+               return (-1);
+
+       if (serio->Mode->BaudRate != sp) {
+               status = EFI_CALL(serio->SetAttributes, serio,
+                   sp, 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=%d\n",
+                           minor(dev), status);
+                       return (-1);
+               }
+               if (com_speed != -1)
+                       printf("\ncom%d: %d baud\n", minor(dev), sp);
+       }
+
+       /* same as comspeed() in libsa/bioscons.c */
+       newsp = com_speed;
+       com_speed = sp;
+
+       return (newsp);
+}
+
+void
+efi_com_init(struct consdev *cn)
+{
+       if (!efi_valid_com(cn->cn_dev))
+               panic("com%d is not probed", minor(cn->cn_dev));
+
+       if (com_speed == -1)
+               comspeed(cn->cn_dev, 9600); /* default speed is 9600 baud */
+}
+
+int
+efi_com_getc(dev_t dev)
+{
+       EFI_STATUS               status;
+       SERIAL_IO_INTERFACE     *serio;
+       UINTN                    sz;
+       u_char                   buf;
+       static u_char            lastchar = 0;
+
+       if (!efi_valid_com(dev & 0x7f))
+               panic("com%d is not probed", minor(dev));
+       serio = serios[minor(dev & 0x7f)];
+
+       if (lastchar != 0) {
+               int r = lastchar;
+               if ((dev & 0x80) == 0)
+                       lastchar = 0;
+               return (r);
+       }
+
+       for (;;) {
+               sz = 1;
+               status = EFI_CALL(serio->Read, serio, &sz, &buf);
+               if (status == EFI_SUCCESS && sz > 0)
+                       break;
+               if (status != EFI_TIMEOUT && EFI_ERROR(status))
+                       panic("Error reading from serial status=%d", status);
+               if (dev & 0x80)
+                       return (0);
+       }
+
+       if (dev & 0x80)
+               lastchar = buf;
+
+       return (buf);
+}
+
+void
+efi_com_putc(dev_t dev, int c)
+{
+       SERIAL_IO_INTERFACE     *serio;
+       UINTN                    sz = 1;
+       u_char                   buf;
+
+       if (!efi_valid_com(dev))
+               panic("com%d is not probed", minor(dev));
+       serio = serios[minor(dev)];
+       buf = c;
+       EFI_CALL(serio->Write, serio, &sz, &buf);
+}
+
 /***********************************************************************
  * Miscellaneous
  ***********************************************************************/
index 84cbcb5..8a07d5b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: efiboot.h,v 1.1 2015/09/02 01:52:25 yasuoka Exp $     */
+/*     $OpenBSD: efiboot.h,v 1.2 2017/05/31 08:40:32 yasuoka Exp $     */
 
 /*
  * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
  */
 
 void    efi_cleanup(void);
-void    efi_cons_probe (struct consdev *);
-void    efi_memprobe (void);
-void    efi_hardprobe (void);
-void    efi_diskprobe (void);
-void    efi_cons_init (struct consdev *);
-int     efi_cons_getc (dev_t);
-void    efi_cons_putc (dev_t, int);
+void    efi_cons_probe(struct consdev *);
+void    efi_memprobe(void);
+void    efi_hardprobe(void);
+void    efi_diskprobe(void);
+void    efi_cons_init(struct consdev *);
+int     efi_cons_getc(dev_t);
+void    efi_cons_putc(dev_t, int);
 int     efi_cons_getshifts(dev_t dev);
+void    efi_com_probe(struct consdev *);
+void    efi_com_init(struct consdev *);
+int     efi_com_getc(dev_t);
+void    efi_com_putc(dev_t, int);
 int     Xvideo_efi(void);
 int     Xexit_efi(void);
 void    efi_makebootargs(void);
index ee1a11c..5d8e389 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dev_i386.c,v 1.19 2017/03/01 12:43:07 patrick Exp $   */
+/*     $OpenBSD: dev_i386.c,v 1.20 2017/05/31 08:40:32 yasuoka Exp $   */
 
 /*
  * Copyright (c) 1996-1999 Michael Shalayeff
@@ -182,10 +182,8 @@ ttydev(char *name)
 int
 cnspeed(dev_t dev, int sp)
 {
-#ifndef EFIBOOT
        if (major(dev) == 8)    /* comN */
                return comspeed(dev, sp);
-#endif
 
        /* pc0 and anything else */
        return 9600;