Hi, The following diff is to support serial console on efiboot.
It uses ACPI UID to identify the port number (com0, com1 and so on) of probed serial interface. But I'm not sure wether com0-com3 are always mapped UID 0-3 as expected. Though I think this is good enough. Comment? diff --git a/sys/arch/amd64/stand/efiboot/conf.c b/sys/arch/amd64/stand/efiboot/conf.c index 0b2933d4cff..913a33e77a6 100644 --- a/sys/arch/amd64/stand/efiboot/conf.c +++ b/sys/arch/amd64/stand/efiboot/conf.c @@ -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; diff --git a/sys/arch/amd64/stand/efiboot/efiboot.c b/sys/arch/amd64/stand/efiboot/efiboot.c index d668258989f..613ede425b6 100644 --- a/sys/arch/amd64/stand/efiboot/efiboot.c +++ b/sys/arch/amd64/stand/efiboot/efiboot.c @@ -501,10 +501,171 @@ 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]; +const int comports[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; + +void +efi_com_probe(struct consdev *cn) +{ + EFI_GUID serio_guid = SERIAL_IO_PROTOCOL; + 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-4 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 == 0x50141d0 /* PNP0501 */) { + uid = dpp.Acpi->UID; + break; + } + } + if (uid < 0) + continue; + + /* Prepare SERIAL_IO_INTERFACE */ + status = EFI_CALL(BS->HandleProtocol, handles[i], &serio_guid, + (void **)&serio); + if (EFI_ERROR(status)) + continue; + if (uid < nitems(serios)) + 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 (0 <= minor(dev) && 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)]; + + if (!efi_valid_com(dev)) + return (-1); + + if (sp > 0 && 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)) { + painc("com%d: SetAttribute() failed with status=%d\n", + minor(dev), status); + } + com_speed = serio->Mode->BaudRate; + } + com_speed = sp; + + return (serio->Mode->BaudRate); +} + +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 */ + + com_addr = comports[minor(cn->cn_dev)]; +} + +int +efi_com_getc(dev_t dev) +{ + EFI_STATUS status; + SERIAL_IO_INTERFACE *serio; + UINTN sz; + u_char buf[1]; + 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[0]; + + return (buf[0]); +} + +void +efi_com_putc(dev_t dev, int c) +{ + SERIAL_IO_INTERFACE *serio; + UINTN sz = 1; + u_char buf[1]; + + if (!efi_valid_com(dev)) + panic("com%d is not probed", minor(dev)); + serio = serios[minor(dev)]; + buf[0] = c; + EFI_CALL(serio->Write, serio, &sz, buf); +} + /*********************************************************************** * Miscellaneous ***********************************************************************/ diff --git a/sys/arch/amd64/stand/efiboot/efiboot.h b/sys/arch/amd64/stand/efiboot/efiboot.h index 84cbcb537a4..fb7abb7c5f4 100644 --- a/sys/arch/amd64/stand/efiboot/efiboot.h +++ b/sys/arch/amd64/stand/efiboot/efiboot.h @@ -17,14 +17,18 @@ */ 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); diff --git a/sys/arch/amd64/stand/libsa/dev_i386.c b/sys/arch/amd64/stand/libsa/dev_i386.c index ee1a11ca6b0..a2b55ec556d 100644 --- a/sys/arch/amd64/stand/libsa/dev_i386.c +++ b/sys/arch/amd64/stand/libsa/dev_i386.c @@ -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;