From 485f6ecd453d8a2fd8b9b9fadea03159d8b50797 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 12:54:16 +0200 Subject: Adding upstream version 2.06. Signed-off-by: Daniel Baumann --- grub-core/term/arc/console.c | 209 ++++++ grub-core/term/arc/serial.c | 147 +++++ grub-core/term/arm/cros.c | 125 ++++ grub-core/term/arm/cros_ec.c | 238 +++++++ grub-core/term/arm/pl050.c | 189 ++++++ grub-core/term/at_keyboard.c | 332 ++++++++++ grub-core/term/efi/console.c | 470 +++++++++++++ grub-core/term/efi/serial.c | 195 ++++++ grub-core/term/gfxterm.c | 1160 +++++++++++++++++++++++++++++++++ grub-core/term/gfxterm_background.c | 190 ++++++ grub-core/term/i386/coreboot/cbmemc.c | 147 +++++ grub-core/term/i386/pc/console.c | 310 +++++++++ grub-core/term/i386/pc/mda_text.c | 13 + grub-core/term/i386/pc/vga_text.c | 288 ++++++++ grub-core/term/ieee1275/console.c | 267 ++++++++ grub-core/term/ieee1275/escc.c | 319 +++++++++ grub-core/term/ieee1275/serial.c | 287 ++++++++ grub-core/term/morse.c | 133 ++++ grub-core/term/ns8250.c | 318 +++++++++ grub-core/term/ps2.c | 387 +++++++++++ grub-core/term/serial.c | 463 +++++++++++++ grub-core/term/spkmodem.c | 141 ++++ grub-core/term/terminfo.c | 796 ++++++++++++++++++++++ grub-core/term/tparm.c | 767 ++++++++++++++++++++++ grub-core/term/uboot/console.c | 132 ++++ grub-core/term/usb_keyboard.c | 471 +++++++++++++ grub-core/term/xen/console.c | 122 ++++ 27 files changed, 8616 insertions(+) create mode 100644 grub-core/term/arc/console.c create mode 100644 grub-core/term/arc/serial.c create mode 100644 grub-core/term/arm/cros.c create mode 100644 grub-core/term/arm/cros_ec.c create mode 100644 grub-core/term/arm/pl050.c create mode 100644 grub-core/term/at_keyboard.c create mode 100644 grub-core/term/efi/console.c create mode 100644 grub-core/term/efi/serial.c create mode 100644 grub-core/term/gfxterm.c create mode 100644 grub-core/term/gfxterm_background.c create mode 100644 grub-core/term/i386/coreboot/cbmemc.c create mode 100644 grub-core/term/i386/pc/console.c create mode 100644 grub-core/term/i386/pc/mda_text.c create mode 100644 grub-core/term/i386/pc/vga_text.c create mode 100644 grub-core/term/ieee1275/console.c create mode 100644 grub-core/term/ieee1275/escc.c create mode 100644 grub-core/term/ieee1275/serial.c create mode 100644 grub-core/term/morse.c create mode 100644 grub-core/term/ns8250.c create mode 100644 grub-core/term/ps2.c create mode 100644 grub-core/term/serial.c create mode 100644 grub-core/term/spkmodem.c create mode 100644 grub-core/term/terminfo.c create mode 100644 grub-core/term/tparm.c create mode 100644 grub-core/term/uboot/console.c create mode 100644 grub-core/term/usb_keyboard.c create mode 100644 grub-core/term/xen/console.c (limited to 'grub-core/term') diff --git a/grub-core/term/arc/console.c b/grub-core/term/arc/console.c new file mode 100644 index 0000000..c944ffc --- /dev/null +++ b/grub-core/term/arc/console.c @@ -0,0 +1,209 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + +/* FIXME: use unicode. */ + +static int +readkey (struct grub_term_input *term __attribute__ ((unused))) +{ + unsigned long count; + char chr; + + if (GRUB_ARC_FIRMWARE_VECTOR->get_read_status (GRUB_ARC_STDIN)) + return -1; + + if (GRUB_ARC_FIRMWARE_VECTOR->read (GRUB_ARC_STDIN, &chr, 1, &count)) + return -1; + if (!count) + return -1; + return chr; +} + +static void +put (struct grub_term_output *term __attribute__ ((unused)), const int c) +{ + unsigned long count; + char chr = c; + + GRUB_ARC_FIRMWARE_VECTOR->write (GRUB_ARC_STDOUT, &chr, 1, &count); +} + +static struct grub_terminfo_output_state grub_console_terminfo_output; + +int +grub_arc_is_device_serial (const char *name, int alt_names) +{ + if (name[0] == '\0') + return 0; + + const char *ptr = name + grub_strlen (name) - 1; + int i; + /* + Recognize: + serial(N) + serial(N)line(M) + */ + for (i = 0; i < 2; i++) + { + if (!alt_names) + { + if (*ptr != ')') + return 0; + ptr--; + } + for (; ptr >= name && grub_isdigit (*ptr); ptr--); + if (ptr < name) + return 0; + if (!alt_names) + { + if (*ptr != '(') + return 0; + ptr--; + } + if (ptr + 1 >= name + sizeof ("serial") - 1 + && grub_memcmp (ptr + 1 - (sizeof ("serial") - 1), + "serial", sizeof ("serial") - 1) == 0) + return 1; + if (!(ptr + 1 >= name + sizeof ("line") - 1 + && grub_memcmp (ptr + 1 - (sizeof ("line") - 1), + "line", sizeof ("line") - 1) == 0)) + return 0; + ptr -= sizeof ("line") - 1; + if (alt_names) + { + if (*ptr != '/') + return 0; + ptr--; + } + } + return 0; +} + +static int +check_is_serial (void) +{ + static int is_serial = -1; + + if (is_serial != -1) + return is_serial; + + const char *consout = 0; + + /* Check for serial. It works unless user manually overrides ConsoleOut + variable. If he does there is nothing we can do. Fortunately failure + isn't critical. + */ + if (GRUB_ARC_SYSTEM_PARAMETER_BLOCK->firmware_vector_length + >= (unsigned) ((char *) (&GRUB_ARC_FIRMWARE_VECTOR->getenvironmentvariable + 1) + - (char *) GRUB_ARC_FIRMWARE_VECTOR) + && GRUB_ARC_FIRMWARE_VECTOR->getenvironmentvariable) + consout = GRUB_ARC_FIRMWARE_VECTOR->getenvironmentvariable ("ConsoleOut"); + if (!consout) + return is_serial = 0; + return is_serial = grub_arc_is_device_serial (consout, 0); +} + +static void +set_console_dimensions (void) +{ + struct grub_arc_display_status *info = NULL; + + if (check_is_serial ()) + { + grub_console_terminfo_output.size.x = 80; + grub_console_terminfo_output.size.y = 24; + return; + } + + if (GRUB_ARC_SYSTEM_PARAMETER_BLOCK->firmware_vector_length + >= (unsigned) ((char *) (&GRUB_ARC_FIRMWARE_VECTOR->getdisplaystatus + 1) + - (char *) GRUB_ARC_FIRMWARE_VECTOR) + && GRUB_ARC_FIRMWARE_VECTOR->getdisplaystatus) + info = GRUB_ARC_FIRMWARE_VECTOR->getdisplaystatus (GRUB_ARC_STDOUT); + if (info) + { + grub_console_terminfo_output.size.x = info->w + 1; + grub_console_terminfo_output.size.y = info->h + 1; + } +} + +static grub_err_t +grub_console_init_output (struct grub_term_output *term) +{ + set_console_dimensions (); + grub_terminfo_output_init (term); + + return 0; +} + +static struct grub_terminfo_input_state grub_console_terminfo_input = + { + .readkey = readkey + }; + +static struct grub_terminfo_output_state grub_console_terminfo_output = + { + .put = put, + .size = { 80, 20 } + }; + +static struct grub_term_input grub_console_term_input = + { + .name = "console", + .init = grub_terminfo_input_init, + .getkey = grub_terminfo_getkey, + .data = &grub_console_terminfo_input + }; + +static struct grub_term_output grub_console_term_output = + { + .name = "console", + .init = grub_console_init_output, + .putchar = grub_terminfo_putchar, + .getxy = grub_terminfo_getxy, + .getwh = grub_terminfo_getwh, + .gotoxy = grub_terminfo_gotoxy, + .cls = grub_terminfo_cls, + .setcolorstate = grub_terminfo_setcolorstate, + .setcursor = grub_terminfo_setcursor, + .flags = GRUB_TERM_CODE_TYPE_ASCII, + .data = &grub_console_terminfo_output, + .progress_update_divisor = GRUB_PROGRESS_FAST + }; + +void +grub_console_init_early (void) +{ + grub_term_register_input ("console", &grub_console_term_input); + grub_term_register_output ("console", &grub_console_term_output); +} + +void +grub_console_init_lately (void) +{ + grub_terminfo_init (); + if (check_is_serial ()) + grub_terminfo_output_register (&grub_console_term_output, "vt100"); + else + grub_terminfo_output_register (&grub_console_term_output, "arc"); +} diff --git a/grub-core/term/arc/serial.c b/grub-core/term/arc/serial.c new file mode 100644 index 0000000..87d1ce8 --- /dev/null +++ b/grub-core/term/arc/serial.c @@ -0,0 +1,147 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +static void +do_real_config (struct grub_serial_port *port) +{ + char *name; + if (port->configured) + return; + + name = grub_arc_alt_name_to_norm (port->name, ""); + + if (GRUB_ARC_FIRMWARE_VECTOR->open (name,GRUB_ARC_FILE_ACCESS_OPEN_RW, + &port->handle)) + port->handle_valid = 0; + else + port->handle_valid = 1; + + port->configured = 1; +} + +/* Fetch a key. */ +static int +serial_hw_fetch (struct grub_serial_port *port) +{ + unsigned long actual; + char c; + + do_real_config (port); + + if (!port->handle_valid) + return -1; + if (GRUB_ARC_FIRMWARE_VECTOR->read (port->handle, &c, + 1, &actual) || actual <= 0) + return -1; + return c; +} + +/* Put a character. */ +static void +serial_hw_put (struct grub_serial_port *port, const int c) +{ + unsigned long actual; + char c0 = c; + + do_real_config (port); + + if (!port->handle_valid) + return; + + GRUB_ARC_FIRMWARE_VECTOR->write (port->handle, &c0, + 1, &actual); +} + +/* Initialize a serial device. PORT is the port number for a serial device. + SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600, + 19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used + for the device. Likewise, PARITY is the type of the parity and + STOP_BIT_LEN is the length of the stop bit. The possible values for + WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as + macros. */ +static grub_err_t +serial_hw_configure (struct grub_serial_port *port __attribute__ ((unused)), + struct grub_serial_config *config __attribute__ ((unused))) +{ + /* FIXME: no ARC serial config available. */ + + return GRUB_ERR_NONE; +} + +struct grub_serial_driver grub_arcserial_driver = + { + .configure = serial_hw_configure, + .fetch = serial_hw_fetch, + .put = serial_hw_put + }; + +const char * +grub_arcserial_add_port (const char *path) +{ + struct grub_serial_port *port; + grub_err_t err; + + port = grub_zalloc (sizeof (*port)); + if (!port) + return NULL; + port->name = grub_strdup (path); + if (!port->name) + return NULL; + + port->driver = &grub_arcserial_driver; + err = grub_serial_config_defaults (port); + if (err) + grub_print_error (); + + grub_serial_register (port); + + return port->name; +} + +static int +dev_iterate (const char *name, + const struct grub_arc_component *comp __attribute__ ((unused)), + void *data __attribute__ ((unused))) +{ + /* We should check consolein/consoleout flags as + well but some implementations are buggy. */ + if ((comp->flags & (GRUB_ARC_COMPONENT_FLAG_IN | GRUB_ARC_COMPONENT_FLAG_OUT)) + != (GRUB_ARC_COMPONENT_FLAG_IN | GRUB_ARC_COMPONENT_FLAG_OUT)) + return 0; + if (!grub_arc_is_device_serial (name, 1)) + return 0; + grub_arcserial_add_port (name); + return 0; +} + +void +grub_arcserial_init (void) +{ + grub_arc_iterate_devs (dev_iterate, 0, 1); +} + diff --git a/grub-core/term/arm/cros.c b/grub-core/term/arm/cros.c new file mode 100644 index 0000000..a17e49c --- /dev/null +++ b/grub-core/term/arm/cros.c @@ -0,0 +1,125 @@ +/* + * GRUB -- GRand Unified Bootloader + * + * Copyright (C) 2012 Google Inc. + * Copyright (C) 2016 Free Software Foundation, Inc. + * + * This is based on depthcharge code. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct grub_ps2_state ps2_state; + +struct grub_cros_ec_keyscan old_scan; + +static const struct grub_fdtbus_dev *cros_ec; + +static grub_uint8_t map_code[GRUB_CROS_EC_KEYSCAN_COLS][GRUB_CROS_EC_KEYSCAN_ROWS]; + +static grub_uint8_t e0_translate[16] = + { + 0x1c, 0x1d, 0x35, 0x00, + 0x38, 0x00, 0x47, 0x48, + 0x49, 0x4b, 0x4d, 0x4f, + 0x50, 0x51, 0x52, 0x53, + }; + +/* If there is a character pending, return it; + otherwise return GRUB_TERM_NO_KEY. */ +static int +grub_cros_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused))) +{ + struct grub_cros_ec_keyscan scan; + int i, j; + if (grub_cros_ec_scan_keyboard (cros_ec, &scan) < 0) + return GRUB_TERM_NO_KEY; + for (i = 0; i < GRUB_CROS_EC_KEYSCAN_COLS; i++) + if (scan.data[i] ^ old_scan.data[i]) + for (j = 0; j < GRUB_CROS_EC_KEYSCAN_ROWS; j++) + if ((scan.data[i] ^ old_scan.data[i]) & (1 << j)) + { + grub_uint8_t code = map_code[i][j]; + int ret; + grub_uint8_t brk = 0; + if (!(scan.data[i] & (1 << j))) + brk = 0x80; + grub_dprintf ("cros_keyboard", "key <%d, %d> code %x\n", i, j, code); + if (code < 0x60) + ret = grub_ps2_process_incoming_byte (&ps2_state, code | brk); + else if (code >= 0x60 && code < 0x70 && e0_translate[code - 0x60]) + { + grub_ps2_process_incoming_byte (&ps2_state, 0xe0); + ret = grub_ps2_process_incoming_byte (&ps2_state, e0_translate[code - 0x60] | brk); + } + else + ret = GRUB_TERM_NO_KEY; + old_scan.data[i] ^= (1 << j); + if (ret != GRUB_TERM_NO_KEY) + return ret; + } + return GRUB_TERM_NO_KEY; +} + +static struct grub_term_input grub_cros_keyboard_term = + { + .name = "cros_keyboard", + .getkey = grub_cros_keyboard_getkey + }; + +static grub_err_t +cros_attach (const struct grub_fdtbus_dev *dev) +{ + grub_size_t keymap_size, i; + const grub_uint8_t *keymap = grub_fdtbus_get_prop (dev, "linux,keymap", &keymap_size); + + if (!dev->parent || !grub_cros_ec_validate (dev->parent)) + return GRUB_ERR_IO; + + if (keymap) + { + for (i = 0; i + 3 < keymap_size; i += 4) + if (keymap[i+1] < GRUB_CROS_EC_KEYSCAN_COLS && keymap[i] < GRUB_CROS_EC_KEYSCAN_ROWS + && keymap[i+2] == 0 && keymap[i+3] < 0x80) + map_code[keymap[i+1]][keymap[i]] = keymap[i+3]; + } + + cros_ec = dev->parent; + ps2_state.current_set = 1; + ps2_state.at_keyboard_status = 0; + grub_term_register_input ("cros_keyboard", &grub_cros_keyboard_term); + return GRUB_ERR_NONE; +} + +static struct grub_fdtbus_driver cros = +{ + .compatible = "google,cros-ec-keyb", + .attach = cros_attach +}; + +void +grub_cros_init (void) +{ + grub_fdtbus_register (&cros); +} diff --git a/grub-core/term/arm/cros_ec.c b/grub-core/term/arm/cros_ec.c new file mode 100644 index 0000000..f414481 --- /dev/null +++ b/grub-core/term/arm/cros_ec.c @@ -0,0 +1,238 @@ +/* + * GRUB -- GRand Unified Bootloader + * + * Copyright (C) 2012 Google Inc. + * Copyright (C) 2016 Free Software Foundation, Inc. + * + * This is based on depthcharge code. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include + +static const grub_uint64_t FRAMING_TIMEOUT_MS = 300; + +static const grub_uint8_t EC_FRAMING_BYTE = 0xec; + +#define EC_CMD_MKBP_STATE 0x60 +#define EC_CMD_VERSION0 0xdc + +static grub_uint64_t last_transfer; + +static void +stop_bus (const struct grub_fdtbus_dev *spi) +{ + spi->driver->stop (spi); + last_transfer = grub_get_time_ms (); +} + +static int +wait_for_frame (const struct grub_fdtbus_dev *spi) +{ + grub_uint64_t start = grub_get_time_ms (); + grub_uint8_t byte; + do + { + if (spi->driver->receive (spi, &byte, 1)) + return -1; + if (byte != EC_FRAMING_BYTE && + grub_get_time_ms () - start > FRAMING_TIMEOUT_MS) + { + grub_dprintf ("cros", "Timeout waiting for framing byte.\n"); + return -1; + } + } + while (byte != EC_FRAMING_BYTE); + return 0; +} + +/* + * Calculate a simple 8-bit checksum of a data block + * + * @param data Data block to checksum + * @param size Size of data block in bytes + * @return checksum value (0 to 255) + */ +static grub_uint8_t +cros_ec_calc_checksum (const void *data, int size) +{ + grub_uint8_t csum; + const grub_uint8_t *bytes = data; + int i; + + for (i = csum = 0; i < size; i++) + csum += bytes[i]; + return csum & 0xff; +} + +enum +{ + /* response, arglen */ + CROS_EC_SPI_IN_HDR_SIZE = 2, + /* version, cmd, arglen */ + CROS_EC_SPI_OUT_HDR_SIZE = 3 +}; + +static grub_uint8_t busbuf[256]; +#define MSG_BYTES ((int)sizeof (busbuf)) + +static int +ec_command (const struct grub_fdtbus_dev *dev, int cmd, int cmd_version, + const void *dout, int dout_len, void *din, int din_len) +{ + const struct grub_fdtbus_dev *spi = dev->parent; + grub_uint8_t *bytes; + + /* Header + data + checksum. */ + grub_uint32_t out_bytes = CROS_EC_SPI_OUT_HDR_SIZE + dout_len + 1; + grub_uint32_t in_bytes = CROS_EC_SPI_IN_HDR_SIZE + din_len + 1; + + /* + * Sanity-check I/O sizes given transaction overhead in internal + * buffers. + */ + if (out_bytes > MSG_BYTES) + { + grub_dprintf ("cros", "Cannot send %d bytes\n", dout_len); + return -1; + } + if (in_bytes > MSG_BYTES) + { + grub_dprintf ("cros", "Cannot receive %d bytes\n", din_len); + return -1; + } + + /* Prepare the output. */ + bytes = busbuf; + *bytes++ = EC_CMD_VERSION0 + cmd_version; + *bytes++ = cmd; + *bytes++ = dout_len; + grub_memcpy (bytes, dout, dout_len); + bytes += dout_len; + + *bytes++ = cros_ec_calc_checksum (busbuf, + CROS_EC_SPI_OUT_HDR_SIZE + dout_len); + + /* Depthcharge uses 200 us here but GRUB timer resolution is only 1ms, + decrease this when we increase timer resolution. */ + while (grub_get_time_ms () - last_transfer < 1) + ; + + if (spi->driver->start (spi)) + return -1; + + /* Allow EC to ramp up clock after being awoken. */ + /* Depthcharge only waits 100 us here but GRUB timer resolution is only 1ms, + decrease this when we increase timer resolution. */ + grub_millisleep (1); + + if (spi->driver->send (spi, busbuf, out_bytes)) + { + stop_bus (spi); + return -1; + } + + /* Wait until the EC is ready. */ + if (wait_for_frame (spi)) + { + stop_bus (spi); + return -1; + } + + /* Read the response code and the data length. */ + bytes = busbuf; + if (spi->driver->receive (spi, bytes, 2)) + { + stop_bus (spi); + return -1; + } + grub_uint8_t result = *bytes++; + grub_uint8_t length = *bytes++; + + /* Make sure there's enough room for the data. */ + if (CROS_EC_SPI_IN_HDR_SIZE + length + 1 > MSG_BYTES) + { + grub_dprintf ("cros", "Received length %#02x too large\n", length); + stop_bus (spi); + return -1; + } + + /* Read the data and the checksum, and finish up. */ + if (spi->driver->receive (spi, bytes, length + 1)) + { + stop_bus (spi); + return -1; + } + bytes += length; + int expected = *bytes++; + stop_bus (spi); + + /* Check the integrity of the response. */ + if (result != 0) + { + grub_dprintf ("cros", "Received bad result code %d\n", result); + return -result; + } + + int csum = cros_ec_calc_checksum (busbuf, + CROS_EC_SPI_IN_HDR_SIZE + length); + + if (csum != expected) + { + grub_dprintf ("cros", "Invalid checksum rx %#02x, calced %#02x\n", + expected, csum); + return -1; + } + + /* If the caller wants the response, copy it out for them. */ + if (length < din_len) + din_len = length; + if (din) + { + grub_memcpy (din, (grub_uint8_t *) busbuf + CROS_EC_SPI_IN_HDR_SIZE, din_len); + } + + return din_len; +} + +int +grub_cros_ec_scan_keyboard (const struct grub_fdtbus_dev *dev, struct grub_cros_ec_keyscan *scan) +{ + if (ec_command (dev, EC_CMD_MKBP_STATE, 0, NULL, 0, scan, + sizeof (*scan)) < (int) sizeof (*scan)) + return -1; + + return 0; +} + +int +grub_cros_ec_validate (const struct grub_fdtbus_dev *dev) +{ + if (!grub_fdtbus_is_compatible("google,cros-ec-spi", dev)) + return 0; + if (!dev->parent) + return 0; + if (!dev->parent->driver) + return 0; + if (!dev->parent->driver->send + || !dev->parent->driver->receive) + return 0; + return 1; +} + diff --git a/grub-core/term/arm/pl050.c b/grub-core/term/arm/pl050.c new file mode 100644 index 0000000..b082243 --- /dev/null +++ b/grub-core/term/arm/pl050.c @@ -0,0 +1,189 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static volatile grub_uint32_t *pl050_regs; + +static struct grub_ps2_state ps2_state; + +static void +keyboard_controller_wait_until_ready (void) +{ + while (! (pl050_regs[1] & 0x40)); +} + +static grub_uint8_t +wait_ack (void) +{ + grub_uint64_t endtime; + grub_uint8_t ack; + + endtime = grub_get_time_ms () + 20; + do + ack = pl050_regs[2]; + while (ack != GRUB_AT_ACK && ack != GRUB_AT_NACK + && grub_get_time_ms () < endtime); + return ack; +} + + +static int +write_mode (int mode) +{ + unsigned i; + for (i = 0; i < GRUB_AT_TRIES; i++) + { + grub_uint8_t ack; + keyboard_controller_wait_until_ready (); + pl050_regs[2] = 0xf0; + keyboard_controller_wait_until_ready (); + pl050_regs[2] = mode; + keyboard_controller_wait_until_ready (); + ack = wait_ack (); + if (ack == GRUB_AT_NACK) + continue; + if (ack == GRUB_AT_ACK) + break; + return 0; + } + + return (i != GRUB_AT_TRIES); +} + +static int +query_mode (void) +{ + grub_uint8_t ret; + int e; + + e = write_mode (0); + if (!e) + return 0; + + keyboard_controller_wait_until_ready (); + + do + ret = pl050_regs[2]; + while (ret == GRUB_AT_ACK); + + /* QEMU translates the set even in no-translate mode. */ + if (ret == 0x43 || ret == 1) + return 1; + if (ret == 0x41 || ret == 2) + return 2; + if (ret == 0x3f || ret == 3) + return 3; + return 0; +} + +static void +set_scancodes (void) +{ + write_mode (2); + ps2_state.current_set = query_mode (); + grub_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set); + if (ps2_state.current_set == 2) + return; + + write_mode (1); + ps2_state.current_set = query_mode (); + grub_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set); + if (ps2_state.current_set == 1) + return; + grub_dprintf ("atkeyb", "no supported scancode set found\n"); +} + +static void +keyboard_controller_led (grub_uint8_t leds) +{ + keyboard_controller_wait_until_ready (); + pl050_regs[2] = 0xed; + keyboard_controller_wait_until_ready (); + pl050_regs[2] = leds & 0x7; +} + +/* If there is a character pending, return it; + otherwise return GRUB_TERM_NO_KEY. */ +static int +grub_pl050_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused))) +{ + grub_uint8_t at_key; + int ret; + grub_uint8_t old_led; + + if (!(pl050_regs[1] & 0x10)) + return -1; + at_key = pl050_regs[2]; + old_led = ps2_state.led_status; + + ret = grub_ps2_process_incoming_byte (&ps2_state, at_key); + if (old_led != ps2_state.led_status) + keyboard_controller_led (ps2_state.led_status); + return ret; +} + +static struct grub_term_input grub_pl050_keyboard_term = + { + .name = "pl050_keyboard", + .getkey = grub_pl050_keyboard_getkey + }; + +static grub_err_t +pl050_attach(const struct grub_fdtbus_dev *dev) +{ + const grub_uint32_t *reg; + reg = grub_fdtbus_get_prop (dev, "reg", 0); + + /* Mouse. Nothing to do. */ + if (grub_be_to_cpu32 (*reg) == 0x7000) + return 0; + + pl050_regs = grub_fdtbus_map_reg (dev, 0, 0); + + if (!grub_fdtbus_is_mapping_valid (pl050_regs)) + return grub_error (GRUB_ERR_IO, "could not map pl050"); + + ps2_state.at_keyboard_status = 0; + set_scancodes (); + keyboard_controller_led (ps2_state.led_status); + + grub_term_register_input ("pl050_keyboard", &grub_pl050_keyboard_term); + return GRUB_ERR_NONE; +} + +struct grub_fdtbus_driver pl050 = +{ + .compatible = "arm,pl050", + .attach = pl050_attach +}; + +void +grub_pl050_init (void) +{ + grub_fdtbus_register (&pl050); +} diff --git a/grub-core/term/at_keyboard.c b/grub-core/term/at_keyboard.c new file mode 100644 index 0000000..5971110 --- /dev/null +++ b/grub-core/term/at_keyboard.c @@ -0,0 +1,332 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_uint8_t grub_keyboard_controller_orig; +static grub_uint8_t grub_keyboard_orig_set; +struct grub_ps2_state ps2_state; + +static int ping_sent; + +static void +grub_keyboard_controller_init (void); + +static void +keyboard_controller_wait_until_ready (void) +{ + /* 50 us would be enough but our current time resolution is 1ms. */ + grub_millisleep (1); + while (! KEYBOARD_COMMAND_ISREADY (grub_inb (KEYBOARD_REG_STATUS))); +} + +static grub_uint8_t +wait_ack (void) +{ + grub_uint64_t endtime; + grub_uint8_t ack; + + endtime = grub_get_time_ms () + 20; + do { + keyboard_controller_wait_until_ready (); + ack = grub_inb (KEYBOARD_REG_DATA); + } while (ack != GRUB_AT_ACK && ack != GRUB_AT_NACK + && grub_get_time_ms () < endtime); + return ack; +} + +static int +at_command (grub_uint8_t data) +{ + unsigned i; + for (i = 0; i < GRUB_AT_TRIES; i++) + { + grub_uint8_t ack; + keyboard_controller_wait_until_ready (); + grub_outb (data, KEYBOARD_REG_STATUS); + ack = wait_ack (); + if (ack == GRUB_AT_NACK) + continue; + if (ack == GRUB_AT_ACK) + break; + return 0; + } + return (i != GRUB_AT_TRIES); +} + +static void +grub_keyboard_controller_write (grub_uint8_t c) +{ + at_command (KEYBOARD_COMMAND_WRITE); + keyboard_controller_wait_until_ready (); + grub_outb (c, KEYBOARD_REG_DATA); +} + +#if defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_QEMU) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS) +#define USE_SCANCODE_SET 1 +#else +#define USE_SCANCODE_SET 0 +#endif + +#if !USE_SCANCODE_SET + +static grub_uint8_t +grub_keyboard_controller_read (void) +{ + at_command (KEYBOARD_COMMAND_READ); + keyboard_controller_wait_until_ready (); + return grub_inb (KEYBOARD_REG_DATA); +} + +#endif + +static int +write_mode (int mode) +{ + unsigned i; + for (i = 0; i < GRUB_AT_TRIES; i++) + { + grub_uint8_t ack; + keyboard_controller_wait_until_ready (); + grub_outb (0xf0, KEYBOARD_REG_DATA); + keyboard_controller_wait_until_ready (); + grub_outb (mode, KEYBOARD_REG_DATA); + keyboard_controller_wait_until_ready (); + ack = wait_ack (); + if (ack == GRUB_AT_NACK) + continue; + if (ack == GRUB_AT_ACK) + break; + return 0; + } + + return (i != GRUB_AT_TRIES); +} + +static int +query_mode (void) +{ + grub_uint8_t ret; + int e; + + e = write_mode (0); + if (!e) + return 0; + + do { + keyboard_controller_wait_until_ready (); + ret = grub_inb (KEYBOARD_REG_DATA); + } while (ret == GRUB_AT_ACK); + /* QEMU translates the set even in no-translate mode. */ + if (ret == 0x43 || ret == 1) + return 1; + if (ret == 0x41 || ret == 2) + return 2; + if (ret == 0x3f || ret == 3) + return 3; + return 0; +} + +static void +set_scancodes (void) +{ + /* You must have visited computer museum. Keyboard without scancode set + knowledge. Assume XT. */ + if (!grub_keyboard_orig_set) + { + grub_dprintf ("atkeyb", "No sets support assumed\n"); + ps2_state.current_set = 1; + return; + } + +#if !USE_SCANCODE_SET + ps2_state.current_set = 1; + return; +#else + + grub_keyboard_controller_write (grub_keyboard_controller_orig + & ~KEYBOARD_AT_TRANSLATE + & ~KEYBOARD_AT_DISABLE); + + keyboard_controller_wait_until_ready (); + grub_outb (KEYBOARD_COMMAND_ENABLE, KEYBOARD_REG_DATA); + + write_mode (2); + ps2_state.current_set = query_mode (); + grub_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set); + if (ps2_state.current_set == 2) + return; + + write_mode (1); + ps2_state.current_set = query_mode (); + grub_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set); + if (ps2_state.current_set == 1) + return; + grub_dprintf ("atkeyb", "no supported scancode set found\n"); +#endif +} + +static void +keyboard_controller_led (grub_uint8_t leds) +{ + keyboard_controller_wait_until_ready (); + grub_outb (0xed, KEYBOARD_REG_DATA); + keyboard_controller_wait_until_ready (); + grub_outb (leds & 0x7, KEYBOARD_REG_DATA); +} + +int +grub_at_keyboard_is_alive (void) +{ + if (ps2_state.current_set != 0) + return 1; + if (ping_sent + && KEYBOARD_COMMAND_ISREADY (grub_inb (KEYBOARD_REG_STATUS)) + && grub_inb (KEYBOARD_REG_DATA) == 0x55) + { + grub_keyboard_controller_init (); + return 1; + } + + if (KEYBOARD_COMMAND_ISREADY (grub_inb (KEYBOARD_REG_STATUS))) + { + grub_outb (0xaa, KEYBOARD_REG_STATUS); + ping_sent = 1; + } + return 0; +} + +/* If there is a character pending, return it; + otherwise return GRUB_TERM_NO_KEY. */ +static int +grub_at_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused))) +{ + grub_uint8_t at_key; + int ret; + grub_uint8_t old_led; + + if (!grub_at_keyboard_is_alive ()) + return GRUB_TERM_NO_KEY; + + if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS))) + return GRUB_TERM_NO_KEY; + at_key = grub_inb (KEYBOARD_REG_DATA); + old_led = ps2_state.led_status; + + ret = grub_ps2_process_incoming_byte (&ps2_state, at_key); + if (old_led != ps2_state.led_status) + keyboard_controller_led (ps2_state.led_status); + return ret; +} + +static void +grub_keyboard_controller_init (void) +{ + ps2_state.at_keyboard_status = 0; + /* Drain input buffer. */ + while (1) + { + keyboard_controller_wait_until_ready (); + if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS))) + break; + keyboard_controller_wait_until_ready (); + grub_inb (KEYBOARD_REG_DATA); + } +#if defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS) + grub_keyboard_controller_orig = 0; + grub_keyboard_orig_set = 2; +#elif defined (GRUB_MACHINE_QEMU) || defined (GRUB_MACHINE_COREBOOT) + /* *BSD relies on those settings. */ + grub_keyboard_controller_orig = KEYBOARD_AT_TRANSLATE; + grub_keyboard_orig_set = 2; +#else + grub_keyboard_controller_orig = grub_keyboard_controller_read (); + grub_keyboard_orig_set = query_mode (); +#endif + set_scancodes (); + keyboard_controller_led (ps2_state.led_status); +} + +static grub_err_t +grub_keyboard_controller_fini (struct grub_term_input *term __attribute__ ((unused))) +{ + if (ps2_state.current_set == 0) + return GRUB_ERR_NONE; + if (grub_keyboard_orig_set) + write_mode (grub_keyboard_orig_set); + grub_keyboard_controller_write (grub_keyboard_controller_orig); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_at_fini_hw (int noreturn __attribute__ ((unused))) +{ + return grub_keyboard_controller_fini (NULL); +} + +static grub_err_t +grub_at_restore_hw (void) +{ + if (ps2_state.current_set == 0) + return GRUB_ERR_NONE; + + /* Drain input buffer. */ + while (1) + { + keyboard_controller_wait_until_ready (); + if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS))) + break; + keyboard_controller_wait_until_ready (); + grub_inb (KEYBOARD_REG_DATA); + } + set_scancodes (); + keyboard_controller_led (ps2_state.led_status); + + return GRUB_ERR_NONE; +} + + +static struct grub_term_input grub_at_keyboard_term = + { + .name = "at_keyboard", + .fini = grub_keyboard_controller_fini, + .getkey = grub_at_keyboard_getkey + }; + +GRUB_MOD_INIT(at_keyboard) +{ + grub_term_register_input ("at_keyboard", &grub_at_keyboard_term); + grub_loader_register_preboot_hook (grub_at_fini_hw, grub_at_restore_hw, + GRUB_LOADER_PREBOOT_HOOK_PRIO_CONSOLE); +} + +GRUB_MOD_FINI(at_keyboard) +{ + grub_keyboard_controller_fini (NULL); + grub_term_unregister_input (&grub_at_keyboard_term); +} diff --git a/grub-core/term/efi/console.c b/grub-core/term/efi/console.c new file mode 100644 index 0000000..2f1ae85 --- /dev/null +++ b/grub-core/term/efi/console.c @@ -0,0 +1,470 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +typedef enum { + GRUB_TEXT_MODE_UNDEFINED = -1, + GRUB_TEXT_MODE_UNAVAILABLE = 0, + GRUB_TEXT_MODE_AVAILABLE +} +grub_text_mode; + +static grub_text_mode text_mode = GRUB_TEXT_MODE_UNDEFINED; +static grub_term_color_state text_colorstate = GRUB_TERM_COLOR_UNDEFINED; + +static grub_uint32_t +map_char (grub_uint32_t c) +{ + /* Map some unicode characters to the EFI character. */ + switch (c) + { + case GRUB_UNICODE_LEFTARROW: + c = GRUB_UNICODE_BLACK_LEFT_TRIANGLE; + break; + case GRUB_UNICODE_UPARROW: + c = GRUB_UNICODE_BLACK_UP_TRIANGLE; + break; + case GRUB_UNICODE_RIGHTARROW: + c = GRUB_UNICODE_BLACK_RIGHT_TRIANGLE; + break; + case GRUB_UNICODE_DOWNARROW: + c = GRUB_UNICODE_BLACK_DOWN_TRIANGLE; + break; + case GRUB_UNICODE_HLINE: + c = GRUB_UNICODE_LIGHT_HLINE; + break; + case GRUB_UNICODE_VLINE: + c = GRUB_UNICODE_LIGHT_VLINE; + break; + case GRUB_UNICODE_CORNER_UL: + c = GRUB_UNICODE_LIGHT_CORNER_UL; + break; + case GRUB_UNICODE_CORNER_UR: + c = GRUB_UNICODE_LIGHT_CORNER_UR; + break; + case GRUB_UNICODE_CORNER_LL: + c = GRUB_UNICODE_LIGHT_CORNER_LL; + break; + case GRUB_UNICODE_CORNER_LR: + c = GRUB_UNICODE_LIGHT_CORNER_LR; + break; + } + + return c; +} + +static void +grub_console_setcolorstate (struct grub_term_output *term + __attribute__ ((unused)), + grub_term_color_state state) +{ + grub_efi_simple_text_output_interface_t *o; + + if (grub_efi_is_finished) + return; + + o = grub_efi_system_table->con_out; + + switch (state) { + case GRUB_TERM_COLOR_STANDARD: + efi_call_2 (o->set_attributes, o, GRUB_TERM_DEFAULT_STANDARD_COLOR + & 0x7f); + break; + case GRUB_TERM_COLOR_NORMAL: + efi_call_2 (o->set_attributes, o, grub_term_normal_color & 0x7f); + break; + case GRUB_TERM_COLOR_HIGHLIGHT: + efi_call_2 (o->set_attributes, o, grub_term_highlight_color & 0x7f); + break; + default: + break; + } +} + +static void +grub_console_setcursor (struct grub_term_output *term __attribute__ ((unused)), + int on) +{ + grub_efi_simple_text_output_interface_t *o; + + if (grub_efi_is_finished) + return; + + o = grub_efi_system_table->con_out; + efi_call_2 (o->enable_cursor, o, on); +} + +static grub_err_t +grub_prepare_for_text_output (struct grub_term_output *term) +{ + if (grub_efi_is_finished) + return GRUB_ERR_BAD_DEVICE; + + if (text_mode != GRUB_TEXT_MODE_UNDEFINED) + return text_mode ? GRUB_ERR_NONE : GRUB_ERR_BAD_DEVICE; + + if (! grub_efi_set_text_mode (1)) + { + /* This really should never happen */ + grub_error (GRUB_ERR_BAD_DEVICE, "cannot set text mode"); + text_mode = GRUB_TEXT_MODE_UNAVAILABLE; + return GRUB_ERR_BAD_DEVICE; + } + + grub_console_setcursor (term, 1); + if (text_colorstate != GRUB_TERM_COLOR_UNDEFINED) + grub_console_setcolorstate (term, text_colorstate); + text_mode = GRUB_TEXT_MODE_AVAILABLE; + return GRUB_ERR_NONE; +} + +static void +grub_console_putchar (struct grub_term_output *term, + const struct grub_unicode_glyph *c) +{ + grub_efi_char16_t str[2 + 30]; + grub_efi_simple_text_output_interface_t *o; + unsigned i, j; + + if (grub_prepare_for_text_output (term) != GRUB_ERR_NONE) + return; + + o = grub_efi_system_table->con_out; + + /* For now, do not try to use a surrogate pair. */ + if (c->base > 0xffff) + str[0] = '?'; + else + str[0] = (grub_efi_char16_t) map_char (c->base & 0xffff); + j = 1; + for (i = 0; i < c->ncomb && j + 1 < ARRAY_SIZE (str); i++) + if (c->base < 0xffff) + str[j++] = grub_unicode_get_comb (c)[i].code; + str[j] = 0; + + /* Should this test be cached? */ + if ((c->base > 0x7f || c->ncomb) + && efi_call_2 (o->test_string, o, str) != GRUB_EFI_SUCCESS) + return; + + efi_call_2 (o->output_string, o, str); +} + +const unsigned efi_codes[] = + { + 0, GRUB_TERM_KEY_UP, GRUB_TERM_KEY_DOWN, GRUB_TERM_KEY_RIGHT, + GRUB_TERM_KEY_LEFT, GRUB_TERM_KEY_HOME, GRUB_TERM_KEY_END, GRUB_TERM_KEY_INSERT, + GRUB_TERM_KEY_DC, GRUB_TERM_KEY_PPAGE, GRUB_TERM_KEY_NPAGE, GRUB_TERM_KEY_F1, + GRUB_TERM_KEY_F2, GRUB_TERM_KEY_F3, GRUB_TERM_KEY_F4, GRUB_TERM_KEY_F5, + GRUB_TERM_KEY_F6, GRUB_TERM_KEY_F7, GRUB_TERM_KEY_F8, GRUB_TERM_KEY_F9, + GRUB_TERM_KEY_F10, GRUB_TERM_KEY_F11, GRUB_TERM_KEY_F12, GRUB_TERM_ESC + }; + +static int +grub_efi_translate_key (grub_efi_input_key_t key) +{ + if (key.scan_code == 0) + { + /* Some firmware implementations use VT100-style codes against the spec. + This is especially likely if driven by serial. + */ + if (key.unicode_char < 0x20 && key.unicode_char != 0 + && key.unicode_char != '\t' && key.unicode_char != '\b' + && key.unicode_char != '\n' && key.unicode_char != '\r') + return GRUB_TERM_CTRL | (key.unicode_char - 1 + 'a'); + else + return key.unicode_char; + } + /* Some devices send enter with scan_code 0x0d (F3) and unicode_char 0x0d. */ + else if (key.scan_code == '\r' && key.unicode_char == '\r') + return key.unicode_char; + else if (key.scan_code < ARRAY_SIZE (efi_codes)) + return efi_codes[key.scan_code]; + + if ((key.unicode_char >= 0x20 && key.unicode_char <= 0x7f) + || key.unicode_char == '\t' || key.unicode_char == '\b' + || key.unicode_char == '\n' || key.unicode_char == '\r') + return key.unicode_char; + + return GRUB_TERM_NO_KEY; +} + +static int +grub_console_getkey_con (struct grub_term_input *term __attribute__ ((unused))) +{ + grub_efi_simple_input_interface_t *i; + grub_efi_input_key_t key; + grub_efi_status_t status; + + i = grub_efi_system_table->con_in; + status = efi_call_2 (i->read_key_stroke, i, &key); + + if (status != GRUB_EFI_SUCCESS) + return GRUB_TERM_NO_KEY; + + return grub_efi_translate_key(key); +} + +/* + * When more then just modifiers are pressed, our getkeystatus() consumes a + * press from the queue, this function buffers the press for the regular + * getkey() so that it does not get lost. + */ +static grub_err_t +grub_console_read_key_stroke ( + grub_efi_simple_text_input_ex_interface_t *text_input, + grub_efi_key_data_t *key_data_ret, int *key_ret, + int consume) +{ + static grub_efi_key_data_t key_data; + grub_efi_status_t status; + int key; + + if (!text_input) + return GRUB_ERR_EOF; + + key = grub_efi_translate_key (key_data.key); + if (key == GRUB_TERM_NO_KEY) { + status = efi_call_2 (text_input->read_key_stroke, text_input, &key_data); + if (status != GRUB_EFI_SUCCESS) + return GRUB_ERR_EOF; + + key = grub_efi_translate_key (key_data.key); + } + + *key_data_ret = key_data; + *key_ret = key; + + if (consume) { + key_data.key.scan_code = 0; + key_data.key.unicode_char = 0; + } + + return GRUB_ERR_NONE; +} + +static int +grub_console_getkey_ex (struct grub_term_input *term) +{ + grub_efi_key_data_t key_data; + grub_efi_uint32_t kss; + grub_err_t err; + int key = -1; + + err = grub_console_read_key_stroke (term->data, &key_data, &key, 1); + if (err != GRUB_ERR_NONE || key == GRUB_TERM_NO_KEY) + return GRUB_TERM_NO_KEY; + + kss = key_data.key_state.key_shift_state; + if (kss & GRUB_EFI_SHIFT_STATE_VALID) + { + if ((kss & GRUB_EFI_LEFT_SHIFT_PRESSED + || kss & GRUB_EFI_RIGHT_SHIFT_PRESSED) + && (key & GRUB_TERM_EXTENDED)) + key |= GRUB_TERM_SHIFT; + if (kss & GRUB_EFI_LEFT_ALT_PRESSED || kss & GRUB_EFI_RIGHT_ALT_PRESSED) + key |= GRUB_TERM_ALT; + if (kss & GRUB_EFI_LEFT_CONTROL_PRESSED + || kss & GRUB_EFI_RIGHT_CONTROL_PRESSED) + key |= GRUB_TERM_CTRL; + } + + return key; +} + +static int +grub_console_getkeystatus (struct grub_term_input *term) +{ + grub_efi_key_data_t key_data; + grub_efi_uint32_t kss; + int key, mods = 0; + + if (grub_efi_is_finished) + return 0; + + if (grub_console_read_key_stroke (term->data, &key_data, &key, 0)) + return 0; + + kss = key_data.key_state.key_shift_state; + if (kss & GRUB_EFI_SHIFT_STATE_VALID) + { + if (kss & GRUB_EFI_LEFT_SHIFT_PRESSED) + mods |= GRUB_TERM_STATUS_LSHIFT; + if (kss & GRUB_EFI_RIGHT_SHIFT_PRESSED) + mods |= GRUB_TERM_STATUS_RSHIFT; + if (kss & GRUB_EFI_LEFT_ALT_PRESSED) + mods |= GRUB_TERM_STATUS_LALT; + if (kss & GRUB_EFI_RIGHT_ALT_PRESSED) + mods |= GRUB_TERM_STATUS_RALT; + if (kss & GRUB_EFI_LEFT_CONTROL_PRESSED) + mods |= GRUB_TERM_STATUS_LCTRL; + if (kss & GRUB_EFI_RIGHT_CONTROL_PRESSED) + mods |= GRUB_TERM_STATUS_RCTRL; + } + + return mods; +} + +static grub_err_t +grub_efi_console_input_init (struct grub_term_input *term) +{ + grub_efi_guid_t text_input_ex_guid = + GRUB_EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; + + if (grub_efi_is_finished) + return 0; + + grub_efi_simple_text_input_ex_interface_t *text_input = term->data; + if (text_input) + return 0; + + text_input = grub_efi_open_protocol(grub_efi_system_table->console_in_handler, + &text_input_ex_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + term->data = (void *)text_input; + + return 0; +} + +static int +grub_console_getkey (struct grub_term_input *term) +{ + if (grub_efi_is_finished) + return 0; + + if (term->data) + return grub_console_getkey_ex(term); + else + return grub_console_getkey_con(term); +} + +static struct grub_term_coordinate +grub_console_getwh (struct grub_term_output *term) +{ + grub_efi_simple_text_output_interface_t *o; + grub_efi_uintn_t columns, rows; + + o = grub_efi_system_table->con_out; + if (grub_prepare_for_text_output (term) != GRUB_ERR_NONE || + efi_call_4 (o->query_mode, o, o->mode->mode, + &columns, &rows) != GRUB_EFI_SUCCESS) + { + /* Why does this fail? */ + columns = 80; + rows = 25; + } + + return (struct grub_term_coordinate) { columns, rows }; +} + +static struct grub_term_coordinate +grub_console_getxy (struct grub_term_output *term __attribute__ ((unused))) +{ + grub_efi_simple_text_output_interface_t *o; + + if (grub_efi_is_finished || text_mode != GRUB_TEXT_MODE_AVAILABLE) + return (struct grub_term_coordinate) { 0, 0 }; + + o = grub_efi_system_table->con_out; + return (struct grub_term_coordinate) { o->mode->cursor_column, o->mode->cursor_row }; +} + +static void +grub_console_gotoxy (struct grub_term_output *term, + struct grub_term_coordinate pos) +{ + grub_efi_simple_text_output_interface_t *o; + + if (grub_prepare_for_text_output (term) != GRUB_ERR_NONE) + return; + + o = grub_efi_system_table->con_out; + efi_call_3 (o->set_cursor_position, o, pos.x, pos.y); +} + +static void +grub_console_cls (struct grub_term_output *term __attribute__ ((unused))) +{ + grub_efi_simple_text_output_interface_t *o; + grub_efi_int32_t orig_attr; + + if (grub_efi_is_finished || text_mode != GRUB_TEXT_MODE_AVAILABLE) + return; + + o = grub_efi_system_table->con_out; + orig_attr = o->mode->attribute; + efi_call_2 (o->set_attributes, o, GRUB_EFI_BACKGROUND_BLACK); + efi_call_1 (o->clear_screen, o); + efi_call_2 (o->set_attributes, o, orig_attr); +} + +static grub_err_t +grub_efi_console_output_fini (struct grub_term_output *term) +{ + if (text_mode != GRUB_TEXT_MODE_AVAILABLE) + return 0; + + grub_console_setcursor (term, 0); + grub_efi_set_text_mode (0); + text_mode = GRUB_TEXT_MODE_UNDEFINED; + return 0; +} + +static struct grub_term_input grub_console_term_input = + { + .name = "console", + .getkey = grub_console_getkey, + .getkeystatus = grub_console_getkeystatus, + .init = grub_efi_console_input_init, + }; + +static struct grub_term_output grub_console_term_output = + { + .name = "console", + .fini = grub_efi_console_output_fini, + .putchar = grub_console_putchar, + .getwh = grub_console_getwh, + .getxy = grub_console_getxy, + .gotoxy = grub_console_gotoxy, + .cls = grub_console_cls, + .setcolorstate = grub_console_setcolorstate, + .setcursor = grub_console_setcursor, + .flags = GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS, + .progress_update_divisor = GRUB_PROGRESS_FAST + }; + +void +grub_console_init (void) +{ + grub_term_register_output ("console", &grub_console_term_output); + grub_term_register_input ("console", &grub_console_term_input); +} + +void +grub_console_fini (void) +{ + grub_term_unregister_input (&grub_console_term_input); + grub_term_unregister_output (&grub_console_term_output); +} diff --git a/grub-core/term/efi/serial.c b/grub-core/term/efi/serial.c new file mode 100644 index 0000000..4c94723 --- /dev/null +++ b/grub-core/term/efi/serial.c @@ -0,0 +1,195 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008,2012 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* GUID. */ +static grub_efi_guid_t serial_io_guid = GRUB_EFI_SERIAL_IO_GUID; + +static void +do_real_config (struct grub_serial_port *port) +{ + grub_efi_status_t status = GRUB_EFI_SUCCESS; + const grub_efi_parity_type_t parities[] = { + [GRUB_SERIAL_PARITY_NONE] = GRUB_EFI_SERIAL_NO_PARITY, + [GRUB_SERIAL_PARITY_ODD] = GRUB_EFI_SERIAL_ODD_PARITY, + [GRUB_SERIAL_PARITY_EVEN] = GRUB_EFI_SERIAL_EVEN_PARITY + }; + const grub_efi_stop_bits_t stop_bits[] = { + [GRUB_SERIAL_STOP_BITS_1] = GRUB_EFI_SERIAL_1_STOP_BIT, + [GRUB_SERIAL_STOP_BITS_1_5] = GRUB_EFI_SERIAL_1_5_STOP_BITS, + [GRUB_SERIAL_STOP_BITS_2] = GRUB_EFI_SERIAL_2_STOP_BITS, + }; + + if (port->configured) + return; + + status = efi_call_7 (port->interface->set_attributes, port->interface, + port->config.speed, + 0, 0, parities[port->config.parity], + port->config.word_len, + stop_bits[port->config.stop_bits]); + if (status != GRUB_EFI_SUCCESS) + port->broken = 1; + + status = efi_call_2 (port->interface->set_control_bits, port->interface, + port->config.rtscts ? 0x4002 : 0x2); + + port->configured = 1; +} + +/* Fetch a key. */ +static int +serial_hw_fetch (struct grub_serial_port *port) +{ + grub_efi_uintn_t bufsize = 1; + char c; + grub_efi_status_t status = GRUB_EFI_SUCCESS; + do_real_config (port); + if (port->broken) + return -1; + + status = efi_call_3 (port->interface->read, port->interface, &bufsize, &c); + if (status != GRUB_EFI_SUCCESS || bufsize == 0) + return -1; + + return c; +} + +/* Put a character. */ +static void +serial_hw_put (struct grub_serial_port *port, const int c) +{ + grub_efi_uintn_t bufsize = 1; + char c0 = c; + + do_real_config (port); + + if (port->broken) + return; + + efi_call_3 (port->interface->write, port->interface, &bufsize, &c0); +} + +/* Initialize a serial device. PORT is the port number for a serial device. + SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600, + 19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used + for the device. Likewise, PARITY is the type of the parity and + STOP_BIT_LEN is the length of the stop bit. The possible values for + WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as + macros. */ +static grub_err_t +serial_hw_configure (struct grub_serial_port *port, + struct grub_serial_config *config) +{ + if (config->parity != GRUB_SERIAL_PARITY_NONE + && config->parity != GRUB_SERIAL_PARITY_ODD + && config->parity != GRUB_SERIAL_PARITY_EVEN) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port parity")); + + if (config->stop_bits != GRUB_SERIAL_STOP_BITS_1 + && config->stop_bits != GRUB_SERIAL_STOP_BITS_1_5 + && config->stop_bits != GRUB_SERIAL_STOP_BITS_2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port stop bits number")); + + if (config->word_len < 5 || config->word_len > 8) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port word length")); + + port->config = *config; + port->configured = 0; + + /* FIXME: should check if the serial terminal was found. */ + + return GRUB_ERR_NONE; +} + +struct grub_serial_driver grub_efiserial_driver = + { + .configure = serial_hw_configure, + .fetch = serial_hw_fetch, + .put = serial_hw_put + }; + +void +grub_efiserial_init (void) +{ + grub_efi_uintn_t num_handles; + grub_efi_handle_t *handles; + grub_efi_handle_t *handle; + int num_serial = 0; + grub_err_t err; + + /* Find handles which support the disk io interface. */ + handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &serial_io_guid, + 0, &num_handles); + if (! handles) + return; + + /* Make a linked list of devices. */ + for (handle = handles; num_handles--; handle++) + { + struct grub_serial_port *port; + struct grub_efi_serial_io_interface *sio; + + sio = grub_efi_open_protocol (*handle, &serial_io_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (! sio) + /* This should not happen... Why? */ + continue; + + port = grub_zalloc (sizeof (*port)); + if (!port) + return; + + port->name = grub_malloc (sizeof ("efiXXXXXXXXXXXXXXXXXXXX")); + if (!port->name) + { + grub_free (port); + return; + } + grub_snprintf (port->name, sizeof ("efiXXXXXXXXXXXXXXXXXXXX"), + "efi%d", num_serial++); + + port->driver = &grub_efiserial_driver; + port->interface = sio; + err = grub_serial_config_defaults (port); + if (err) + grub_print_error (); + + grub_serial_register (port); + } + + grub_free (handles); + + return; +} diff --git a/grub-core/term/gfxterm.c b/grub-core/term/gfxterm.c new file mode 100644 index 0000000..b40fcce --- /dev/null +++ b/grub-core/term/gfxterm.c @@ -0,0 +1,1160 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define DEFAULT_VIDEO_MODE "auto" +#define DEFAULT_BORDER_WIDTH 10 + +#define DEFAULT_STANDARD_COLOR 0x07 + +struct grub_dirty_region +{ + int top_left_x; + int top_left_y; + int bottom_right_x; + int bottom_right_y; +}; + +struct grub_colored_char +{ + /* An Unicode codepoint. */ + struct grub_unicode_glyph code; + + /* Color values. */ + grub_video_color_t fg_color; + grub_video_color_t bg_color; +}; + +struct grub_virtual_screen +{ + /* Dimensions of the virtual screen in pixels. */ + unsigned int width; + unsigned int height; + + /* Offset in the display in pixels. */ + unsigned int offset_x; + unsigned int offset_y; + + /* TTY Character sizes in pixes. */ + unsigned int normal_char_width; + unsigned int normal_char_height; + + /* Virtual screen TTY size in characters. */ + unsigned int columns; + unsigned int rows; + + /* Current cursor location in characters. */ + unsigned int cursor_x; + unsigned int cursor_y; + + /* Current cursor state. */ + int cursor_state; + + /* Font settings. */ + grub_font_t font; + + /* Terminal color settings. */ + grub_uint8_t standard_color_setting; + grub_uint8_t term_color; + + /* Color settings. */ + grub_video_color_t fg_color; + grub_video_color_t bg_color; + grub_video_color_t bg_color_display; + + /* Text buffer for virtual screen. Contains (columns * rows) number + of entries. */ + struct grub_colored_char *text_buffer; + + int total_scroll; + + int functional; +}; + +struct grub_gfxterm_window +{ + unsigned x; + unsigned y; + unsigned width; + unsigned height; + int double_repaint; +}; + +static struct grub_video_render_target *render_target; +void (*grub_gfxterm_decorator_hook) (void) = NULL; +static struct grub_gfxterm_window window; +static struct grub_virtual_screen virtual_screen; +static int repaint_scheduled = 0; +static int repaint_was_scheduled = 0; + +static void destroy_window (void); + +static struct grub_video_render_target *text_layer; + +struct grub_gfxterm_background grub_gfxterm_background; + +static struct grub_dirty_region dirty_region; + +static void dirty_region_reset (void); + +static int dirty_region_is_empty (void); + +static void dirty_region_add (int x, int y, + unsigned int width, unsigned int height); + +static unsigned int calculate_normal_character_width (grub_font_t font); + +static unsigned char calculate_character_width (struct grub_font_glyph *glyph); + +static void grub_gfxterm_refresh (struct grub_term_output *term __attribute__ ((unused))); + +static grub_size_t +grub_gfxterm_getcharwidth (struct grub_term_output *term __attribute__ ((unused)), + const struct grub_unicode_glyph *c); + +static void +set_term_color (grub_uint8_t term_color) +{ + struct grub_video_render_target *old_target; + + /* Save previous target and switch to text layer. */ + grub_video_get_active_render_target (&old_target); + grub_video_set_active_render_target (text_layer); + + /* Map terminal color to text layer compatible video colors. */ + virtual_screen.fg_color = grub_video_map_color(term_color & 0x0f); + + /* Special case: use black as transparent color. */ + if (((term_color >> 4) & 0x0f) == 0) + { + virtual_screen.bg_color = grub_video_map_rgba(0, 0, 0, 0); + } + else + { + virtual_screen.bg_color = grub_video_map_color((term_color >> 4) & 0x0f); + } + + /* Restore previous target. */ + grub_video_set_active_render_target (old_target); +} + +static void +clear_char (struct grub_colored_char *c) +{ + grub_unicode_destroy_glyph (&c->code); + grub_unicode_set_glyph_from_code (&c->code, ' '); + c->fg_color = virtual_screen.fg_color; + c->bg_color = virtual_screen.bg_color; +} + +static void +grub_virtual_screen_free (void) +{ + virtual_screen.functional = 0; + + /* If virtual screen has been allocated, free it. */ + if (virtual_screen.text_buffer != 0) + { + unsigned i; + for (i = 0; + i < virtual_screen.columns * virtual_screen.rows; + i++) + grub_unicode_destroy_glyph (&virtual_screen.text_buffer[i].code); + grub_free (virtual_screen.text_buffer); + } + + /* Reset virtual screen data. */ + grub_memset (&virtual_screen, 0, sizeof (virtual_screen)); + + /* Free render targets. */ + grub_video_delete_render_target (text_layer); + text_layer = 0; +} + +static grub_err_t +grub_virtual_screen_setup (unsigned int x, unsigned int y, + unsigned int width, unsigned int height, + grub_font_t font) +{ + unsigned int i; + + /* Free old virtual screen. */ + grub_virtual_screen_free (); + + /* Initialize with default data. */ + virtual_screen.font = font; + virtual_screen.width = width; + virtual_screen.height = height; + virtual_screen.offset_x = x; + virtual_screen.offset_y = y; + virtual_screen.normal_char_width = + calculate_normal_character_width (virtual_screen.font); + virtual_screen.normal_char_height = + grub_font_get_max_char_height (virtual_screen.font); + if (virtual_screen.normal_char_height == 0) + virtual_screen.normal_char_height = 16; + virtual_screen.cursor_x = 0; + virtual_screen.cursor_y = 0; + virtual_screen.cursor_state = 1; + virtual_screen.total_scroll = 0; + + /* Calculate size of text buffer. */ + virtual_screen.columns = virtual_screen.width / virtual_screen.normal_char_width; + virtual_screen.rows = virtual_screen.height / virtual_screen.normal_char_height; + + /* + * There must be a minimum number of rows and columns for the screen to + * make sense. Arbitrarily pick half of 80x24. If either dimensions is 0 + * we would allocate 0 bytes for the text_buffer. + */ + if (virtual_screen.columns < 40 || virtual_screen.rows < 12) + return grub_error (GRUB_ERR_BAD_FONT, + "font: glyphs too large to fit on screen"); + + /* Allocate memory for text buffer. */ + virtual_screen.text_buffer = + (struct grub_colored_char *) grub_malloc (virtual_screen.columns + * virtual_screen.rows + * sizeof (*virtual_screen.text_buffer)); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + /* Create new render target for text layer. */ + grub_video_create_render_target (&text_layer, + virtual_screen.width, + virtual_screen.height, + GRUB_VIDEO_MODE_TYPE_INDEX_COLOR + | GRUB_VIDEO_MODE_TYPE_ALPHA); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + /* As we want to have colors compatible with rendering target, + we can only have those after mode is initialized. */ + grub_video_set_active_render_target (text_layer); + + virtual_screen.standard_color_setting = DEFAULT_STANDARD_COLOR; + + virtual_screen.term_color = virtual_screen.standard_color_setting; + + set_term_color (virtual_screen.term_color); + + grub_video_set_active_render_target (render_target); + + virtual_screen.bg_color_display = + grub_video_map_rgba_color (grub_gfxterm_background.default_bg_color); + + /* Clear out text buffer. */ + for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++) + { + virtual_screen.text_buffer[i].code.ncomb = 0; + clear_char (&(virtual_screen.text_buffer[i])); + } + if (grub_errno) + return grub_errno; + + virtual_screen.functional = 1; + + return GRUB_ERR_NONE; +} + +void +grub_gfxterm_schedule_repaint (void) +{ + repaint_scheduled = 1; +} + +grub_err_t +grub_gfxterm_set_window (struct grub_video_render_target *target, + int x, int y, int width, int height, + int double_repaint, + grub_font_t font, int border_width) +{ + /* Clean up any prior instance. */ + destroy_window (); + + /* Set the render target. */ + render_target = target; + + /* Create virtual screen. */ + if (grub_virtual_screen_setup (border_width, border_width, + width - 2 * border_width, + height - 2 * border_width, + font) + != GRUB_ERR_NONE) + { + return grub_errno; + } + + /* Set window bounds. */ + window.x = x; + window.y = y; + window.width = width; + window.height = height; + window.double_repaint = double_repaint; + + dirty_region_reset (); + grub_gfxterm_schedule_repaint (); + + return grub_errno; +} + +static grub_err_t +grub_gfxterm_fullscreen (void) +{ + const char *font_name; + struct grub_video_mode_info mode_info; + grub_video_color_t color; + grub_err_t err; + int double_redraw; + grub_font_t font; + + err = grub_video_get_info (&mode_info); + /* Figure out what mode we ended up. */ + if (err) + return err; + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + double_redraw = mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED + && !(mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); + + /* Make sure screen is set to the default background color. */ + color = grub_video_map_rgba_color (grub_gfxterm_background.default_bg_color); + grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height); + if (double_redraw) + { + grub_video_swap_buffers (); + grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height); + } + + /* Select the font to use. */ + font_name = grub_env_get ("gfxterm_font"); + if (! font_name) + font_name = ""; /* Allow fallback to any font. */ + + font = grub_font_get (font_name); + if (!font) + return grub_error (GRUB_ERR_BAD_FONT, "no font loaded"); + + grub_gfxterm_decorator_hook = NULL; + + return grub_gfxterm_set_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY, + 0, 0, mode_info.width, mode_info.height, + double_redraw, + font, DEFAULT_BORDER_WIDTH); +} + +static grub_err_t +grub_gfxterm_term_init (struct grub_term_output *term __attribute__ ((unused))) +{ + char *tmp; + grub_err_t err; + const char *modevar; + + /* Parse gfxmode environment variable if set. */ + modevar = grub_env_get ("gfxmode"); + if (! modevar || *modevar == 0) + err = grub_video_set_mode (DEFAULT_VIDEO_MODE, + GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0); + else + { + tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar); + if (!tmp) + return grub_errno; + err = grub_video_set_mode (tmp, GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0); + grub_free (tmp); + } + + if (err) + return err; + + err = grub_gfxterm_fullscreen (); + if (err) + grub_video_restore (); + + return err; +} + +static void +destroy_window (void) +{ + grub_virtual_screen_free (); +} + +static grub_err_t +grub_gfxterm_term_fini (struct grub_term_output *term __attribute__ ((unused))) +{ + unsigned i; + destroy_window (); + grub_video_restore (); + + for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++) + { + grub_unicode_destroy_glyph (&virtual_screen.text_buffer[i].code); + virtual_screen.text_buffer[i].code.ncomb = 0; + virtual_screen.text_buffer[i].code.base = 0; + } + + /* Clear error state. */ + grub_errno = GRUB_ERR_NONE; + return GRUB_ERR_NONE; +} + +static void +redraw_screen_rect (unsigned int x, unsigned int y, + unsigned int width, unsigned int height) +{ + grub_video_color_t color; + grub_video_rect_t saved_view; + + grub_video_set_active_render_target (render_target); + /* Save viewport and set it to our window. */ + grub_video_get_viewport ((unsigned *) &saved_view.x, + (unsigned *) &saved_view.y, + (unsigned *) &saved_view.width, + (unsigned *) &saved_view.height); + grub_video_set_viewport (window.x, window.y, window.width, window.height); + + if (grub_gfxterm_background.bitmap) + { + /* Render bitmap as background. */ + grub_video_blit_bitmap (grub_gfxterm_background.bitmap, + GRUB_VIDEO_BLIT_REPLACE, x, y, + x, y, + width, height); + + /* If bitmap is smaller than requested blit area, use background + color. */ + color = virtual_screen.bg_color_display; + + /* Fill right side of the bitmap if needed. */ + if ((x + width >= grub_gfxterm_background.bitmap->mode_info.width) + && (y < grub_gfxterm_background.bitmap->mode_info.height)) + { + int w = (x + width) - grub_gfxterm_background.bitmap->mode_info.width; + int h = height; + unsigned int tx = x; + + if (y + height >= grub_gfxterm_background.bitmap->mode_info.height) + { + h = grub_gfxterm_background.bitmap->mode_info.height - y; + } + + if (grub_gfxterm_background.bitmap->mode_info.width > tx) + { + tx = grub_gfxterm_background.bitmap->mode_info.width; + } + + /* Render background layer. */ + grub_video_fill_rect (color, tx, y, w, h); + } + + /* Fill bottom side of the bitmap if needed. */ + if (y + height >= grub_gfxterm_background.bitmap->mode_info.height) + { + int h = (y + height) - grub_gfxterm_background.bitmap->mode_info.height; + unsigned int ty = y; + + if (grub_gfxterm_background.bitmap->mode_info.height > ty) + { + ty = grub_gfxterm_background.bitmap->mode_info.height; + } + + /* Render background layer. */ + grub_video_fill_rect (color, x, ty, width, h); + } + } + else + { + /* Render background layer. */ + color = virtual_screen.bg_color_display; + grub_video_fill_rect (color, x, y, width, height); + } + + if (grub_gfxterm_background.blend_text_bg) + /* Render text layer as blended. */ + grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_BLEND, x, y, + x - virtual_screen.offset_x, + y - virtual_screen.offset_y, + width, height); + else + /* Render text layer as replaced (to get texts background color). */ + grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_REPLACE, x, y, + x - virtual_screen.offset_x, + y - virtual_screen.offset_y, + width, height); + + /* Restore saved viewport. */ + grub_video_set_viewport (saved_view.x, saved_view.y, + saved_view.width, saved_view.height); + grub_video_set_active_render_target (render_target); +} + +static void +dirty_region_reset (void) +{ + dirty_region.top_left_x = -1; + dirty_region.top_left_y = -1; + dirty_region.bottom_right_x = -1; + dirty_region.bottom_right_y = -1; + repaint_was_scheduled = 0; +} + +static int +dirty_region_is_empty (void) +{ + if ((dirty_region.top_left_x == -1) + || (dirty_region.top_left_y == -1) + || (dirty_region.bottom_right_x == -1) + || (dirty_region.bottom_right_y == -1)) + return 1; + return 0; +} + +static void +dirty_region_add_real (int x, int y, unsigned int width, unsigned int height) +{ + if (dirty_region_is_empty ()) + { + dirty_region.top_left_x = x; + dirty_region.top_left_y = y; + dirty_region.bottom_right_x = x + width - 1; + dirty_region.bottom_right_y = y + height - 1; + } + else + { + if (x < dirty_region.top_left_x) + dirty_region.top_left_x = x; + if (y < dirty_region.top_left_y) + dirty_region.top_left_y = y; + if ((x + (int)width - 1) > dirty_region.bottom_right_x) + dirty_region.bottom_right_x = x + width - 1; + if ((y + (int)height - 1) > dirty_region.bottom_right_y) + dirty_region.bottom_right_y = y + height - 1; + } +} + +static void +dirty_region_add (int x, int y, unsigned int width, unsigned int height) +{ + if ((width == 0) || (height == 0)) + return; + + if (repaint_scheduled) + { + dirty_region_add_real (0, 0, + window.width, window.height); + repaint_scheduled = 0; + repaint_was_scheduled = 1; + } + dirty_region_add_real (x, y, width, height); +} + +static void +dirty_region_add_virtualscreen (void) +{ + /* Mark virtual screen as dirty. */ + dirty_region_add (virtual_screen.offset_x, virtual_screen.offset_y, + virtual_screen.width, virtual_screen.height); +} + + +static void +dirty_region_redraw (void) +{ + int x; + int y; + int width; + int height; + + if (dirty_region_is_empty ()) + return; + + x = dirty_region.top_left_x; + y = dirty_region.top_left_y; + + width = dirty_region.bottom_right_x - x + 1; + height = dirty_region.bottom_right_y - y + 1; + + if (repaint_was_scheduled && grub_gfxterm_decorator_hook) + grub_gfxterm_decorator_hook (); + + redraw_screen_rect (x, y, width, height); +} + +static inline void +paint_char (unsigned cx, unsigned cy) +{ + struct grub_colored_char *p; + struct grub_font_glyph *glyph; + grub_video_color_t color; + grub_video_color_t bgcolor; + unsigned int x; + unsigned int y; + int ascent; + unsigned int height; + unsigned int width; + + if (cy + virtual_screen.total_scroll >= virtual_screen.rows) + return; + + /* Find out active character. */ + p = (virtual_screen.text_buffer + + cx + (cy * virtual_screen.columns)); + + if (!p->code.base) + return; + + /* Get glyph for character. */ + glyph = grub_font_construct_glyph (virtual_screen.font, &p->code); + if (!glyph) + { + grub_errno = GRUB_ERR_NONE; + return; + } + ascent = grub_font_get_ascent (virtual_screen.font); + + width = virtual_screen.normal_char_width * calculate_character_width(glyph); + height = virtual_screen.normal_char_height; + + color = p->fg_color; + bgcolor = p->bg_color; + + x = cx * virtual_screen.normal_char_width; + y = (cy + virtual_screen.total_scroll) * virtual_screen.normal_char_height; + + /* Render glyph to text layer. */ + grub_video_set_active_render_target (text_layer); + grub_video_fill_rect (bgcolor, x, y, width, height); + grub_font_draw_glyph (glyph, color, x, y + ascent); + grub_video_set_active_render_target (render_target); + + /* Mark character to be drawn. */ + dirty_region_add (virtual_screen.offset_x + x, virtual_screen.offset_y + y, + width, height); +} + +static inline void +write_char (void) +{ + paint_char (virtual_screen.cursor_x, virtual_screen.cursor_y); +} + +static inline void +draw_cursor (int show) +{ + unsigned int x; + unsigned int y; + unsigned int width; + unsigned int height; + unsigned int ascent; + grub_video_color_t color; + + write_char (); + + if (!show) + return; + + if (virtual_screen.cursor_y + virtual_screen.total_scroll + >= virtual_screen.rows) + return; + + /* Ensure that cursor doesn't go outside of character box. */ + ascent = grub_font_get_ascent(virtual_screen.font); + if (ascent > virtual_screen.normal_char_height - 2) + ascent = virtual_screen.normal_char_height - 2; + + /* Determine cursor properties and position on text layer. */ + x = virtual_screen.cursor_x * virtual_screen.normal_char_width; + width = virtual_screen.normal_char_width; + color = virtual_screen.fg_color; + y = ((virtual_screen.cursor_y + virtual_screen.total_scroll) + * virtual_screen.normal_char_height + + ascent); + height = 2; + + /* Render cursor to text layer. */ + grub_video_set_active_render_target (text_layer); + grub_video_fill_rect (color, x, y, width, height); + grub_video_set_active_render_target (render_target); + + /* Mark cursor to be redrawn. */ + dirty_region_add (virtual_screen.offset_x + x, + virtual_screen.offset_y + y, + width, height); +} + +static void +real_scroll (void) +{ + unsigned int i, j, was_scroll; + grub_video_color_t color; + + if (!virtual_screen.total_scroll) + return; + + /* If we have bitmap, re-draw screen, otherwise scroll physical screen too. */ + if (grub_gfxterm_background.bitmap) + { + /* Scroll physical screen. */ + grub_video_set_active_render_target (text_layer); + color = virtual_screen.bg_color; + grub_video_scroll (color, 0, -virtual_screen.normal_char_height + * virtual_screen.total_scroll); + + /* Mark virtual screen to be redrawn. */ + dirty_region_add_virtualscreen (); + } + else + { + grub_video_rect_t saved_view; + + /* Remove cursor. */ + draw_cursor (0); + + grub_video_set_active_render_target (render_target); + + i = window.double_repaint ? 2 : 1; + + color = virtual_screen.bg_color_display; + + while (i--) + { + /* Save viewport and set it to our window. */ + grub_video_get_viewport ((unsigned *) &saved_view.x, + (unsigned *) &saved_view.y, + (unsigned *) &saved_view.width, + (unsigned *) &saved_view.height); + + grub_video_set_viewport (window.x, window.y, window.width, + window.height); + + /* Clear new border area. */ + grub_video_fill_rect (color, + virtual_screen.offset_x, + virtual_screen.offset_y, + virtual_screen.width, + virtual_screen.normal_char_height + * virtual_screen.total_scroll); + + grub_video_set_active_render_target (render_target); + dirty_region_redraw (); + + /* Scroll physical screen. */ + grub_video_scroll (color, 0, -virtual_screen.normal_char_height + * virtual_screen.total_scroll); + + /* Restore saved viewport. */ + grub_video_set_viewport (saved_view.x, saved_view.y, + saved_view.width, saved_view.height); + + if (i) + grub_video_swap_buffers (); + } + dirty_region_reset (); + + /* Scroll physical screen. */ + grub_video_set_active_render_target (text_layer); + color = virtual_screen.bg_color; + grub_video_scroll (color, 0, -virtual_screen.normal_char_height + * virtual_screen.total_scroll); + + grub_video_set_active_render_target (render_target); + + } + + was_scroll = virtual_screen.total_scroll; + virtual_screen.total_scroll = 0; + + if (was_scroll > virtual_screen.rows) + was_scroll = virtual_screen.rows; + + /* Draw shadow part. */ + for (i = virtual_screen.rows - was_scroll; + i < virtual_screen.rows; i++) + for (j = 0; j < virtual_screen.columns; j++) + paint_char (j, i); + + /* Draw cursor if visible. */ + if (virtual_screen.cursor_state) + draw_cursor (1); +} + +static void +scroll_up (void) +{ + unsigned int i; + + /* Clear first line in text buffer. */ + for (i = 0; i < virtual_screen.columns; i++) + grub_unicode_destroy_glyph (&virtual_screen.text_buffer[i].code); + + /* Scroll text buffer with one line to up. */ + grub_memmove (virtual_screen.text_buffer, + virtual_screen.text_buffer + virtual_screen.columns, + sizeof (*virtual_screen.text_buffer) + * virtual_screen.columns + * (virtual_screen.rows - 1)); + + /* Clear last line in text buffer. */ + for (i = virtual_screen.columns * (virtual_screen.rows - 1); + i < virtual_screen.columns * virtual_screen.rows; + i++) + clear_char (&(virtual_screen.text_buffer[i])); + + virtual_screen.total_scroll++; +} + +static void +grub_gfxterm_putchar (struct grub_term_output *term, + const struct grub_unicode_glyph *c) +{ + if (!virtual_screen.functional) + return; + + if (c->base == '\a') + /* FIXME */ + return; + + /* Erase current cursor, if any. */ + if (virtual_screen.cursor_state) + draw_cursor (0); + + if (c->base == '\b' || c->base == '\n' || c->base == '\r') + { + switch (c->base) + { + case '\b': + if (virtual_screen.cursor_x > 0) + virtual_screen.cursor_x--; + break; + + case '\n': + if (virtual_screen.cursor_y >= virtual_screen.rows - 1) + scroll_up (); + else + virtual_screen.cursor_y++; + break; + + case '\r': + virtual_screen.cursor_x = 0; + break; + } + } + else + { + struct grub_colored_char *p; + unsigned char char_width; + + /* Calculate actual character width for glyph. This is number of + times of normal_font_width. */ + char_width = grub_gfxterm_getcharwidth (term, c); + + /* If we are about to exceed line length, wrap to next line. */ + if (virtual_screen.cursor_x + char_width > virtual_screen.columns) + { + if (virtual_screen.cursor_y >= virtual_screen.rows - 1) + scroll_up (); + else + virtual_screen.cursor_y++; + } + + /* Find position on virtual screen, and fill information. */ + p = (virtual_screen.text_buffer + + virtual_screen.cursor_x + + virtual_screen.cursor_y * virtual_screen.columns); + grub_unicode_destroy_glyph (&p->code); + grub_unicode_set_glyph (&p->code, c); + grub_errno = GRUB_ERR_NONE; + p->fg_color = virtual_screen.fg_color; + p->bg_color = virtual_screen.bg_color; + + /* If we have large glyph, add fixup info. */ + if (char_width > 1) + { + unsigned i; + + for (i = 1; i < char_width && p + i < + virtual_screen.text_buffer + virtual_screen.columns + * virtual_screen.rows; i++) + { + grub_unicode_destroy_glyph (&p[i].code); + p[i].code.base = 0; + } + } + + /* Draw glyph. */ + write_char (); + + /* Make sure we scroll screen when needed and wrap line correctly. */ + virtual_screen.cursor_x += char_width; + if (virtual_screen.cursor_x >= virtual_screen.columns) + { + virtual_screen.cursor_x = 0; + + if (virtual_screen.cursor_y >= virtual_screen.rows - 1) + scroll_up (); + else + virtual_screen.cursor_y++; + } + } + + /* Redraw cursor if it should be visible. */ + /* Note: This will redraw the character as well, which means that the + above call to write_char is redundant when the cursor is showing. */ + if (virtual_screen.cursor_state) + draw_cursor (1); +} + +/* Use ASCII characters to determine normal character width. */ +static unsigned int +calculate_normal_character_width (grub_font_t font) +{ + struct grub_font_glyph *glyph; + unsigned int width = 0; + unsigned int i; + + /* Get properties of every printable ASCII character. */ + for (i = 32; i < 127; i++) + { + glyph = grub_font_get_glyph (font, i); + + /* Skip unknown characters. Should never happen on normal conditions. */ + if (! glyph) + continue; + + if (glyph->device_width > width) + width = glyph->device_width; + } + if (!width) + return 8; + + return width; +} + +static unsigned char +calculate_character_width (struct grub_font_glyph *glyph) +{ + if (! glyph || glyph->device_width == 0) + return 1; + + return (glyph->device_width + + (virtual_screen.normal_char_width - 1)) + / virtual_screen.normal_char_width; +} + +static grub_size_t +grub_gfxterm_getcharwidth (struct grub_term_output *term __attribute__ ((unused)), + const struct grub_unicode_glyph *c) +{ + int dev_width; + dev_width = grub_font_get_constructed_device_width (virtual_screen.font, c); + + if (dev_width == 0) + return 1; + + return (dev_width + (virtual_screen.normal_char_width - 1)) + / virtual_screen.normal_char_width; +} + +static struct grub_term_coordinate +grub_virtual_screen_getwh (struct grub_term_output *term __attribute__ ((unused))) +{ + return (struct grub_term_coordinate) { virtual_screen.columns, virtual_screen.rows }; +} + +static struct grub_term_coordinate +grub_virtual_screen_getxy (struct grub_term_output *term __attribute__ ((unused))) +{ + return (struct grub_term_coordinate) { virtual_screen.cursor_x, virtual_screen.cursor_y }; +} + +static void +grub_gfxterm_gotoxy (struct grub_term_output *term __attribute__ ((unused)), + struct grub_term_coordinate pos) +{ + if (pos.x >= virtual_screen.columns) + pos.x = virtual_screen.columns - 1; + + if (pos.y >= virtual_screen.rows) + pos.y = virtual_screen.rows - 1; + + /* Erase current cursor, if any. */ + if (virtual_screen.cursor_state) + draw_cursor (0); + + virtual_screen.cursor_x = pos.x; + virtual_screen.cursor_y = pos.y; + + /* Draw cursor if visible. */ + if (virtual_screen.cursor_state) + draw_cursor (1); +} + +static void +grub_virtual_screen_cls (struct grub_term_output *term __attribute__ ((unused))) +{ + grub_uint32_t i; + + for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++) + clear_char (&(virtual_screen.text_buffer[i])); + + virtual_screen.cursor_x = virtual_screen.cursor_y = 0; +} + +static void +grub_gfxterm_cls (struct grub_term_output *term) +{ + grub_video_color_t color; + + /* Clear virtual screen. */ + grub_virtual_screen_cls (term); + + /* Clear text layer. */ + grub_video_set_active_render_target (text_layer); + color = virtual_screen.bg_color; + grub_video_fill_rect (color, 0, 0, + virtual_screen.width, virtual_screen.height); + grub_video_set_active_render_target (render_target); + + /* Mark virtual screen to be redrawn. */ + dirty_region_add_virtualscreen (); + + grub_gfxterm_refresh (term); +} + +static void +grub_virtual_screen_setcolorstate (struct grub_term_output *term __attribute__ ((unused)), + grub_term_color_state state) +{ + switch (state) + { + case GRUB_TERM_COLOR_STANDARD: + virtual_screen.term_color = virtual_screen.standard_color_setting; + break; + + case GRUB_TERM_COLOR_NORMAL: + virtual_screen.term_color = grub_term_normal_color; + break; + + case GRUB_TERM_COLOR_HIGHLIGHT: + virtual_screen.term_color = grub_term_highlight_color; + break; + + default: + break; + } + + /* Change color to virtual terminal. */ + set_term_color (virtual_screen.term_color); +} + +static void +grub_gfxterm_setcursor (struct grub_term_output *term __attribute__ ((unused)), + int on) +{ + if (virtual_screen.cursor_state != on) + { + if (virtual_screen.cursor_state) + draw_cursor (0); + else + draw_cursor (1); + + virtual_screen.cursor_state = on; + } +} + +static void +grub_gfxterm_refresh (struct grub_term_output *term __attribute__ ((unused))) +{ + real_scroll (); + + /* Redraw only changed regions. */ + dirty_region_redraw (); + + grub_video_swap_buffers (); + + if (window.double_repaint) + dirty_region_redraw (); + dirty_region_reset (); +} + +static struct grub_term_output grub_video_term = + { + .name = "gfxterm", + .init = grub_gfxterm_term_init, + .fini = grub_gfxterm_term_fini, + .putchar = grub_gfxterm_putchar, + .getcharwidth = grub_gfxterm_getcharwidth, + .getwh = grub_virtual_screen_getwh, + .getxy = grub_virtual_screen_getxy, + .gotoxy = grub_gfxterm_gotoxy, + .cls = grub_gfxterm_cls, + .setcolorstate = grub_virtual_screen_setcolorstate, + .setcursor = grub_gfxterm_setcursor, + .refresh = grub_gfxterm_refresh, + .fullscreen = grub_gfxterm_fullscreen, + .flags = GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS, + .progress_update_divisor = GRUB_PROGRESS_SLOW, + .next = 0 + }; + +void +grub_gfxterm_video_update_color (void) +{ + struct grub_video_render_target *old_target; + + grub_video_get_active_render_target (&old_target); + grub_video_set_active_render_target (text_layer); + virtual_screen.bg_color = grub_video_map_rgba_color (grub_gfxterm_background.default_bg_color); + grub_video_set_active_render_target (old_target); + virtual_screen.bg_color_display = + grub_video_map_rgba_color (grub_gfxterm_background.default_bg_color); +} + +void +grub_gfxterm_get_dimensions (unsigned *width, unsigned *height) +{ + *width = window.width; + *height = window.height; +} + +GRUB_MOD_INIT(gfxterm) +{ + grub_term_register_output ("gfxterm", &grub_video_term); +} + +GRUB_MOD_FINI(gfxterm) +{ + grub_term_unregister_output (&grub_video_term); +} diff --git a/grub-core/term/gfxterm_background.c b/grub-core/term/gfxterm_background.c new file mode 100644 index 0000000..8da71a8 --- /dev/null +++ b/grub-core/term/gfxterm_background.c @@ -0,0 +1,190 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008,2009,2013 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Option array indices. */ +enum + { + BACKGROUND_CMD_ARGINDEX_MODE = 0 + }; + +static const struct grub_arg_option background_image_cmd_options[] = + { + {"mode", 'm', 0, N_("Background image mode."), + /* TRANSLATORS: This refers to background image mode (stretched or + in left-top corner). Note that GRUB will accept only original + keywords stretch and normal, not the translated ones. + So please put both in translation + e.g. stretch(=%STRETCH%)|normal(=%NORMAL%). + The percents mark the translated version. Since many people + may not know the word stretch or normal I recommend + putting the translation either here or in "Background image mode." + string. */ + N_("stretch|normal"), + ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} + }; + +static grub_err_t +grub_gfxterm_background_image_cmd (grub_extcmd_context_t ctxt, + int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; + + /* Check that we have video adapter active. */ + if (grub_video_get_info(NULL) != GRUB_ERR_NONE) + return grub_errno; + + /* Destroy existing background bitmap if loaded. */ + if (grub_gfxterm_background.bitmap) + { + grub_video_bitmap_destroy (grub_gfxterm_background.bitmap); + grub_gfxterm_background.bitmap = 0; + grub_gfxterm_background.blend_text_bg = 0; + + /* Mark whole screen as dirty. */ + grub_gfxterm_schedule_repaint (); + } + + /* If filename was provided, try to load that. */ + if (argc >= 1) + { + /* Try to load new one. */ + grub_video_bitmap_load (&grub_gfxterm_background.bitmap, args[0]); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + /* Determine if the bitmap should be scaled to fit the screen. */ + if (!state[BACKGROUND_CMD_ARGINDEX_MODE].set + || grub_strcmp (state[BACKGROUND_CMD_ARGINDEX_MODE].arg, + "stretch") == 0) + { + unsigned int width, height; + grub_gfxterm_get_dimensions (&width, &height); + if (width + != grub_video_bitmap_get_width (grub_gfxterm_background.bitmap) + || height + != grub_video_bitmap_get_height (grub_gfxterm_background.bitmap)) + { + struct grub_video_bitmap *scaled_bitmap; + grub_video_bitmap_create_scaled (&scaled_bitmap, + width, + height, + grub_gfxterm_background.bitmap, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + if (grub_errno == GRUB_ERR_NONE) + { + /* Replace the original bitmap with the scaled one. */ + grub_video_bitmap_destroy (grub_gfxterm_background.bitmap); + grub_gfxterm_background.bitmap = scaled_bitmap; + } + } + } + + /* If bitmap was loaded correctly, display it. */ + if (grub_gfxterm_background.bitmap) + { + grub_gfxterm_background.blend_text_bg = 1; + + /* Mark whole screen as dirty. */ + grub_gfxterm_schedule_repaint (); + } + } + + /* All was ok. */ + grub_errno = GRUB_ERR_NONE; + return grub_errno; +} + +static grub_err_t +grub_gfxterm_background_color_cmd (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + /* Check that we have video adapter active. */ + if (grub_video_get_info (NULL) != GRUB_ERR_NONE) + return grub_errno; + + if (grub_video_parse_color (args[0], + &grub_gfxterm_background.default_bg_color) + != GRUB_ERR_NONE) + return grub_errno; + + /* Destroy existing background bitmap if loaded. */ + if (grub_gfxterm_background.bitmap) + { + grub_video_bitmap_destroy (grub_gfxterm_background.bitmap); + grub_gfxterm_background.bitmap = 0; + + /* Mark whole screen as dirty. */ + grub_gfxterm_schedule_repaint (); + } + + /* Set the background and border colors. The background color needs to be + compatible with the text layer. */ + grub_gfxterm_video_update_color (); + grub_gfxterm_background.blend_text_bg = 1; + + /* Mark whole screen as dirty. */ + grub_gfxterm_schedule_repaint (); + + return GRUB_ERR_NONE; +} + +static grub_extcmd_t background_image_cmd_handle; +static grub_command_t background_color_cmd_handle; + +GRUB_MOD_INIT(gfxterm_background) +{ + background_image_cmd_handle = + grub_register_extcmd ("background_image", + grub_gfxterm_background_image_cmd, 0, + N_("[-m (stretch|normal)] FILE"), + N_("Load background image for active terminal."), + background_image_cmd_options); + background_color_cmd_handle = + grub_register_command ("background_color", + grub_gfxterm_background_color_cmd, + N_("COLOR"), + N_("Set background color for active terminal.")); +} + +GRUB_MOD_FINI(gfxterm_background) +{ + grub_unregister_command (background_color_cmd_handle); + grub_unregister_extcmd (background_image_cmd_handle); +} diff --git a/grub-core/term/i386/coreboot/cbmemc.c b/grub-core/term/i386/coreboot/cbmemc.c new file mode 100644 index 0000000..cea9b84 --- /dev/null +++ b/grub-core/term/i386/coreboot/cbmemc.c @@ -0,0 +1,147 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define CURSOR_MASK ((1 << 28) - 1) +#define OVERFLOW (1 << 31) + +struct grub_linuxbios_cbmemc +{ + grub_uint32_t size; + grub_uint32_t cursor; + char body[0]; +}; + +static struct grub_linuxbios_cbmemc *cbmemc; + +static void +put (struct grub_term_output *term __attribute__ ((unused)), const int c) +{ + grub_uint32_t flags, cursor; + if (!cbmemc) + return; + flags = cbmemc->cursor & ~CURSOR_MASK; + cursor = cbmemc->cursor & CURSOR_MASK; + if (cursor >= cbmemc->size) + return; + cbmemc->body[cursor++] = c; + if (cursor >= cbmemc->size) + { + cursor = 0; + flags |= OVERFLOW; + } + cbmemc->cursor = flags | cursor; +} + +struct grub_terminfo_output_state grub_cbmemc_terminfo_output = + { + .put = put, + .size = { 80, 24 } + }; + +static struct grub_term_output grub_cbmemc_term_output = + { + .name = "cbmemc", + .init = grub_terminfo_output_init, + .fini = 0, + .putchar = grub_terminfo_putchar, + .getxy = grub_terminfo_getxy, + .getwh = grub_terminfo_getwh, + .gotoxy = grub_terminfo_gotoxy, + .cls = grub_terminfo_cls, + .setcolorstate = grub_terminfo_setcolorstate, + .setcursor = grub_terminfo_setcursor, + .flags = GRUB_TERM_CODE_TYPE_ASCII, + .data = &grub_cbmemc_terminfo_output, + .progress_update_divisor = GRUB_PROGRESS_NO_UPDATE + }; + +static int +iterate_linuxbios_table (grub_linuxbios_table_item_t table_item, + void *data __attribute__ ((unused))) +{ + if (table_item->tag != GRUB_LINUXBIOS_MEMBER_CBMEMC) + return 0; + cbmemc = (struct grub_linuxbios_cbmemc *) (grub_addr_t) + *(grub_uint64_t *) (table_item + 1); + return 1; +} + +static grub_err_t +grub_cmd_cbmemc (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char *argv[] __attribute__ ((unused))) +{ + grub_size_t size, cursor; + struct grub_linuxbios_cbmemc *real_cbmemc; + + if (!cbmemc) + return grub_error (GRUB_ERR_IO, "no CBMEM console found"); + + real_cbmemc = cbmemc; + cbmemc = 0; + cursor = real_cbmemc->cursor & CURSOR_MASK; + if (!(real_cbmemc->cursor & OVERFLOW) && cursor < real_cbmemc->size) + size = cursor; + else + size = real_cbmemc->size; + if (real_cbmemc->cursor & OVERFLOW) + { + if (cursor > size) + cursor = 0; + grub_xnputs(real_cbmemc->body + cursor, size - cursor); + grub_xnputs(real_cbmemc->body, cursor); + } + else + grub_xnputs(real_cbmemc->body, size); + cbmemc = real_cbmemc; + return 0; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT (cbmemc) +{ + grub_linuxbios_table_iterate (iterate_linuxbios_table, 0); + + if (cbmemc) + grub_term_register_output ("cbmemc", &grub_cbmemc_term_output); + + cmd = + grub_register_command ("cbmemc", grub_cmd_cbmemc, + 0, N_("Show CBMEM console content.")); +} + + +GRUB_MOD_FINI (cbmemc) +{ + grub_term_unregister_output (&grub_cbmemc_term_output); + grub_unregister_command (cmd); +} diff --git a/grub-core/term/i386/pc/console.c b/grub-core/term/i386/pc/console.c new file mode 100644 index 0000000..f6142a2 --- /dev/null +++ b/grub-core/term/i386/pc/console.c @@ -0,0 +1,310 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2005,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include + +static grub_uint8_t grub_console_cur_color = 0x7; + +static void +int10_9 (grub_uint8_t ch, grub_uint16_t n) +{ + struct grub_bios_int_registers regs; + + regs.eax = ch | 0x0900; + regs.ebx = grub_console_cur_color & 0xff; + regs.ecx = n; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); +} + +/* + * BIOS call "INT 10H Function 03h" to get cursor position + * Call with %ah = 0x03 + * %bh = page + * Returns %ch = starting scan line + * %cl = ending scan line + * %dh = row (0 is top) + * %dl = column (0 is left) + */ + + +static struct grub_term_coordinate +grub_console_getxy (struct grub_term_output *term __attribute__ ((unused))) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0x0300; + regs.ebx = 0; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + + return (struct grub_term_coordinate) { + (regs.edx & 0xff), ((regs.edx & 0xff00) >> 8) }; +} + +/* + * BIOS call "INT 10H Function 02h" to set cursor position + * Call with %ah = 0x02 + * %bh = page + * %dh = row (0 is top) + * %dl = column (0 is left) + */ +static void +grub_console_gotoxy (struct grub_term_output *term __attribute__ ((unused)), + struct grub_term_coordinate pos) +{ + struct grub_bios_int_registers regs; + + /* set page to 0 */ + regs.ebx = 0; + regs.eax = 0x0200; + regs.edx = (pos.y << 8) | pos.x; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); +} + +/* + * + * Put the character C on the console. Because GRUB wants to write a + * character with an attribute, this implementation is a bit tricky. + * If C is a control character (CR, LF, BEL, BS), use INT 10, AH = 0Eh + * (TELETYPE OUTPUT). Otherwise, use INT 10, AH = 9 to write character + * with attributes and advance cursor. If we are on the last column, + * let BIOS to wrap line correctly. + */ +static void +grub_console_putchar_real (grub_uint8_t c) +{ + struct grub_bios_int_registers regs; + struct grub_term_coordinate pos; + + if (c == 7 || c == 8 || c == 0xa || c == 0xd) + { + regs.eax = c | 0x0e00; + regs.ebx = 0x0001; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + return; + } + + /* get the current position */ + pos = grub_console_getxy (NULL); + + /* write the character with the attribute */ + int10_9 (c, 1); + + /* check the column with the width */ + if (pos.x >= 79) + { + grub_console_putchar_real (0x0d); + grub_console_putchar_real (0x0a); + } + else + grub_console_gotoxy (NULL, (struct grub_term_coordinate) { pos.x + 1, + pos.y }); +} + +static void +grub_console_putchar (struct grub_term_output *term __attribute__ ((unused)), + const struct grub_unicode_glyph *c) +{ + grub_console_putchar_real (c->base); +} + +/* + * BIOS call "INT 10H Function 09h" to write character and attribute + * Call with %ah = 0x09 + * %al = (character) + * %bh = (page number) + * %bl = (attribute) + * %cx = (number of times) + */ +static void +grub_console_cls (struct grub_term_output *term) +{ + /* move the cursor to the beginning */ + grub_console_gotoxy (term, (struct grub_term_coordinate) { 0, 0 }); + + /* write spaces to the entire screen */ + int10_9 (' ', 80 * 25); + + /* move back the cursor */ + grub_console_gotoxy (term, (struct grub_term_coordinate) { 0, 0 }); +} + +/* + * void grub_console_setcursor (int on) + * BIOS call "INT 10H Function 01h" to set cursor type + * Call with %ah = 0x01 + * %ch = cursor starting scanline + * %cl = cursor ending scanline + */ +static void +grub_console_setcursor (struct grub_term_output *term __attribute__ ((unused)), + int on) +{ + static grub_uint16_t console_cursor_shape = 0; + struct grub_bios_int_registers regs; + + /* check if the standard cursor shape has already been saved */ + if (!console_cursor_shape) + { + regs.eax = 0x0300; + regs.ebx = 0; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + console_cursor_shape = regs.ecx; + if ((console_cursor_shape >> 8) >= (console_cursor_shape & 0xff)) + console_cursor_shape = 0x0d0e; + } + /* set %cx to the designated cursor shape */ + regs.ecx = on ? console_cursor_shape : 0x2000; + regs.eax = 0x0100; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); +} + +/* + * if there is a character pending, return it; otherwise return -1 + * BIOS call "INT 16H Function 01H" to check whether a character is pending + * Call with %ah = 0x1 + * Return: + * If key waiting to be input: + * %ah = keyboard scan code + * %al = ASCII character + * Zero flag = clear + * else + * Zero flag = set + * BIOS call "INT 16H Function 00H" to read character from keyboard + * Call with %ah = 0x0 + * Return: %ah = keyboard scan code + * %al = ASCII character + */ + +static int +grub_console_getkey (struct grub_term_input *term __attribute__ ((unused))) +{ + const grub_uint16_t bypass_table[] = { + 0x0100 | GRUB_TERM_ESC, 0x0f00 | GRUB_TERM_TAB, 0x0e00 | GRUB_TERM_BACKSPACE, 0x1c00 | '\r', 0x1c00 | '\n' + }; + struct grub_bios_int_registers regs; + unsigned i; + + /* + * Due to a bug in apple's bootcamp implementation, INT 16/AH = 0 would + * cause the machine to hang at the second keystroke. However, we can + * work around this problem by ensuring the presence of keystroke with + * INT 16/AH = 1 before calling INT 16/AH = 0. + */ + + regs.eax = 0x0100; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x16, ®s); + if (regs.flags & GRUB_CPU_INT_FLAGS_ZERO) + return GRUB_TERM_NO_KEY; + + regs.eax = 0x0000; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x16, ®s); + if (!(regs.eax & 0xff)) + return ((regs.eax >> 8) & 0xff) | GRUB_TERM_EXTENDED; + + if ((regs.eax & 0xff) >= ' ') + return regs.eax & 0xff; + + for (i = 0; i < ARRAY_SIZE (bypass_table); i++) + if (bypass_table[i] == (regs.eax & 0xffff)) + return regs.eax & 0xff; + + return (regs.eax & 0xff) + (('a' - 1) | GRUB_TERM_CTRL); +} + +static const struct grub_machine_bios_data_area *bios_data_area = + (struct grub_machine_bios_data_area *) GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR; + +static int +grub_console_getkeystatus (struct grub_term_input *term __attribute__ ((unused))) +{ + /* conveniently GRUB keystatus is modelled after BIOS one. */ + return bios_data_area->keyboard_flag_lower & ~0x80; +} + +static struct grub_term_coordinate +grub_console_getwh (struct grub_term_output *term __attribute__ ((unused))) +{ + return (struct grub_term_coordinate) { 80, 25 }; +} + +static void +grub_console_setcolorstate (struct grub_term_output *term + __attribute__ ((unused)), + grub_term_color_state state) +{ + switch (state) { + case GRUB_TERM_COLOR_STANDARD: + grub_console_cur_color = GRUB_TERM_DEFAULT_STANDARD_COLOR & 0x7f; + break; + case GRUB_TERM_COLOR_NORMAL: + grub_console_cur_color = grub_term_normal_color & 0x7f; + break; + case GRUB_TERM_COLOR_HIGHLIGHT: + grub_console_cur_color = grub_term_highlight_color & 0x7f; + break; + default: + break; + } +} + +static struct grub_term_input grub_console_term_input = + { + .name = "console", + .getkey = grub_console_getkey, + .getkeystatus = grub_console_getkeystatus + }; + +static struct grub_term_output grub_console_term_output = + { + .name = "console", + .putchar = grub_console_putchar, + .getwh = grub_console_getwh, + .getxy = grub_console_getxy, + .gotoxy = grub_console_gotoxy, + .cls = grub_console_cls, + .setcolorstate = grub_console_setcolorstate, + .setcursor = grub_console_setcursor, + .flags = GRUB_TERM_CODE_TYPE_CP437, + .progress_update_divisor = GRUB_PROGRESS_FAST + }; + +void +grub_console_init (void) +{ + grub_term_register_output ("console", &grub_console_term_output); + grub_term_register_input ("console", &grub_console_term_input); +} + +void +grub_console_fini (void) +{ + grub_term_unregister_input (&grub_console_term_input); + grub_term_unregister_output (&grub_console_term_output); +} diff --git a/grub-core/term/i386/pc/mda_text.c b/grub-core/term/i386/pc/mda_text.c new file mode 100644 index 0000000..c3a5a17 --- /dev/null +++ b/grub-core/term/i386/pc/mda_text.c @@ -0,0 +1,13 @@ +#define MODE_MDA 1 +#include "vga_text.c" + +GRUB_MOD_INIT(mda_text) +{ + grub_term_register_output ("mda_text", &grub_vga_text_term); +} + +GRUB_MOD_FINI(mda_text) +{ + grub_term_unregister_output (&grub_vga_text_term); +} + diff --git a/grub-core/term/i386/pc/vga_text.c b/grub-core/term/i386/pc/vga_text.c new file mode 100644 index 0000000..88fecc5 --- /dev/null +++ b/grub-core/term/i386/pc/vga_text.c @@ -0,0 +1,288 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007, 2008, 2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include + +#if defined (GRUB_MACHINE_COREBOOT) +#include +#endif + +/* MODESET is used for testing to force monochrome or colour mode. + You shouldn't use mda_text on vga. + */ +#ifdef MODESET +#include +#endif + +#if defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS) || defined (GRUB_MACHINE_MULTIBOOT) +#include +#endif + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define COLS 80 +#define ROWS 25 + +static struct grub_term_coordinate grub_curr_pos; + +#ifdef __mips__ +#define VGA_TEXT_SCREEN ((grub_uint16_t *) 0xb00b8000) +#define cr_read grub_vga_cr_read +#define cr_write grub_vga_cr_write +#elif defined (MODE_MDA) +#define VGA_TEXT_SCREEN ((grub_uint16_t *) 0xb0000) +#define cr_read grub_vga_cr_bw_read +#define cr_write grub_vga_cr_bw_write +#else +#define VGA_TEXT_SCREEN ((grub_uint16_t *) 0xb8000) +#define cr_read grub_vga_cr_read +#define cr_write grub_vga_cr_write +#endif + +static grub_uint8_t cur_color = 0x7; + +static void +screen_write_char (int x, int y, short c) +{ + VGA_TEXT_SCREEN[y * COLS + x] = grub_cpu_to_le16 (c); +} + +static short +screen_read_char (int x, int y) +{ + return grub_le_to_cpu16 (VGA_TEXT_SCREEN[y * COLS + x]); +} + +static void +update_cursor (void) +{ + unsigned int pos = grub_curr_pos.y * COLS + grub_curr_pos.x; + cr_write (pos >> 8, GRUB_VGA_CR_CURSOR_ADDR_HIGH); + cr_write (pos & 0xFF, GRUB_VGA_CR_CURSOR_ADDR_LOW); +} + +static void +inc_y (void) +{ + grub_curr_pos.x = 0; + if (grub_curr_pos.y < ROWS - 1) + grub_curr_pos.y++; + else + { + int x, y; + for (y = 0; y < ROWS - 1; y++) + for (x = 0; x < COLS; x++) + screen_write_char (x, y, screen_read_char (x, y + 1)); + for (x = 0; x < COLS; x++) + screen_write_char (x, ROWS - 1, ' ' | (cur_color << 8)); + } +} + +static void +inc_x (void) +{ + if (grub_curr_pos.x >= COLS - 1) + inc_y (); + else + grub_curr_pos.x++; +} + +static void +grub_vga_text_putchar (struct grub_term_output *term __attribute__ ((unused)), + const struct grub_unicode_glyph *c) +{ + switch (c->base) + { + case '\b': + if (grub_curr_pos.x != 0) + screen_write_char (grub_curr_pos.x--, grub_curr_pos.y, ' '); + break; + case '\n': + inc_y (); + break; + case '\r': + grub_curr_pos.x = 0; + break; + default: + screen_write_char (grub_curr_pos.x, grub_curr_pos.y, + c->base | (cur_color << 8)); + inc_x (); + } + + update_cursor (); +} + +static struct grub_term_coordinate +grub_vga_text_getxy (struct grub_term_output *term __attribute__ ((unused))) +{ + return grub_curr_pos; +} + +static void +grub_vga_text_gotoxy (struct grub_term_output *term __attribute__ ((unused)), + struct grub_term_coordinate pos) +{ + grub_curr_pos = pos; + update_cursor (); +} + +static void +grub_vga_text_cls (struct grub_term_output *term) +{ + int i; + for (i = 0; i < ROWS * COLS; i++) + VGA_TEXT_SCREEN[i] = grub_cpu_to_le16 (' ' | (cur_color << 8)); + grub_vga_text_gotoxy (term, (struct grub_term_coordinate) { 0, 0 }); +} + +static void +grub_vga_text_setcursor (struct grub_term_output *term __attribute__ ((unused)), + int on) +{ + grub_uint8_t old; + old = cr_read (GRUB_VGA_CR_CURSOR_START); + if (on) + cr_write (old & ~GRUB_VGA_CR_CURSOR_START_DISABLE, + GRUB_VGA_CR_CURSOR_START); + else + cr_write (old | GRUB_VGA_CR_CURSOR_START_DISABLE, + GRUB_VGA_CR_CURSOR_START); +} + +static grub_err_t +grub_vga_text_init_real (struct grub_term_output *term) +{ +#ifdef MODESET + struct grub_bios_int_registers regs; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + +#ifdef MODE_MDA + regs.eax = 7; +#else + regs.eax = 3; +#endif + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); +#endif + grub_vga_text_cls (term); + return 0; +} + +static grub_err_t +grub_vga_text_fini_real (struct grub_term_output *term) +{ +#ifdef MODESET + struct grub_bios_int_registers regs; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + + regs.eax = 3; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); +#endif + grub_vga_text_cls (term); + return 0; +} + +static struct grub_term_coordinate +grub_vga_text_getwh (struct grub_term_output *term __attribute__ ((unused))) +{ + return (struct grub_term_coordinate) { 80, 25 }; +} + +#ifndef MODE_MDA + +static void +grub_vga_text_setcolorstate (struct grub_term_output *term __attribute__ ((unused)), + grub_term_color_state state) +{ + switch (state) { + case GRUB_TERM_COLOR_STANDARD: + cur_color = GRUB_TERM_DEFAULT_STANDARD_COLOR & 0x7f; + break; + case GRUB_TERM_COLOR_NORMAL: + cur_color = grub_term_normal_color & 0x7f; + break; + case GRUB_TERM_COLOR_HIGHLIGHT: + cur_color = grub_term_highlight_color & 0x7f; + break; + default: + break; + } +} + +#else +static void +grub_vga_text_setcolorstate (struct grub_term_output *term __attribute__ ((unused)), + grub_term_color_state state) +{ + switch (state) { + case GRUB_TERM_COLOR_STANDARD: + cur_color = 0x07; + break; + case GRUB_TERM_COLOR_NORMAL: + cur_color = 0x07; + break; + case GRUB_TERM_COLOR_HIGHLIGHT: + cur_color = 0x70; + break; + default: + break; + } +} +#endif + +static struct grub_term_output grub_vga_text_term = + { +#ifdef MODE_MDA + .name = "mda_text", +#else + .name = "vga_text", +#endif + .init = grub_vga_text_init_real, + .fini = grub_vga_text_fini_real, + .putchar = grub_vga_text_putchar, + .getwh = grub_vga_text_getwh, + .getxy = grub_vga_text_getxy, + .gotoxy = grub_vga_text_gotoxy, + .cls = grub_vga_text_cls, + .setcolorstate = grub_vga_text_setcolorstate, + .setcursor = grub_vga_text_setcursor, + .flags = GRUB_TERM_CODE_TYPE_CP437, + .progress_update_divisor = GRUB_PROGRESS_FAST + }; + +#ifndef MODE_MDA + +GRUB_MOD_INIT(vga_text) +{ +#ifdef GRUB_MACHINE_COREBOOT + if (!grub_video_coreboot_fbtable) +#endif + grub_term_register_output ("vga_text", &grub_vga_text_term); +} + +GRUB_MOD_FINI(vga_text) +{ + grub_term_unregister_output (&grub_vga_text_term); +} + +#endif diff --git a/grub-core/term/ieee1275/console.c b/grub-core/term/ieee1275/console.c new file mode 100644 index 0000000..7e797a7 --- /dev/null +++ b/grub-core/term/ieee1275/console.c @@ -0,0 +1,267 @@ +/* console.c -- Open Firmware console for GRUB. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static grub_ieee1275_ihandle_t stdout_ihandle; +static grub_ieee1275_ihandle_t stdin_ihandle; + +extern struct grub_terminfo_output_state grub_console_terminfo_output; + +struct color +{ + int red; + int green; + int blue; +}; + +/* Use serial colors as they are default on most firmwares and some firmwares + ignore set-color!. Additionally output may be redirected to serial. */ +static struct color colors[] = + { + // {R, G, B} + {0x00, 0x00, 0x00}, // 0 = black + {0xA8, 0x00, 0x00}, // 1 = red + {0x00, 0xA8, 0x00}, // 2 = green + {0xFE, 0xFE, 0x54}, // 3 = yellow + {0x00, 0x00, 0xA8}, // 4 = blue + {0xA8, 0x00, 0xA8}, // 5 = magenta + {0x00, 0xA8, 0xA8}, // 6 = cyan + {0xFE, 0xFE, 0xFE} // 7 = white + }; + +static void +put (struct grub_term_output *term __attribute__ ((unused)), const int c) +{ + char chr = c; + + grub_ieee1275_write (stdout_ihandle, &chr, 1, 0); +} + +static int +readkey (struct grub_term_input *term __attribute__ ((unused))) +{ + grub_uint8_t c; + grub_ssize_t actual = 0; + + grub_ieee1275_read (stdin_ihandle, &c, 1, &actual); + if (actual > 0) + return c; + return -1; +} + +static void +grub_console_dimensions (void) +{ + grub_ieee1275_ihandle_t options; + grub_ieee1275_phandle_t stdout_phandle; + char val[1024]; + + /* Always assume 80x24 on serial since screen-#rows/screen-#columns is often + garbage for such devices. */ + if (! grub_ieee1275_instance_to_package (stdout_ihandle, + &stdout_phandle) + && ! grub_ieee1275_package_to_path (stdout_phandle, + val, sizeof (val) - 1, 0)) + { + grub_ieee1275_ihandle_t stdout_options; + val[sizeof (val) - 1] = 0; + + if (! grub_ieee1275_finddevice (val, &stdout_options) + && ! grub_ieee1275_get_property (stdout_options, "device_type", + val, sizeof (val) - 1, 0)) + { + val[sizeof (val) - 1] = 0; + if (grub_strcmp (val, "serial") == 0) + { + grub_console_terminfo_output.size.x = 80; + grub_console_terminfo_output.size.y = 24; + return; + } + } + } + + if (! grub_ieee1275_finddevice ("/options", &options) + && options != (grub_ieee1275_ihandle_t) -1) + { + if (! grub_ieee1275_get_property (options, "screen-#columns", + val, sizeof (val) - 1, 0)) + { + val[sizeof (val) - 1] = 0; + grub_console_terminfo_output.size.x + = (grub_uint8_t) grub_strtoul (val, 0, 10); + } + if (! grub_ieee1275_get_property (options, "screen-#rows", + val, sizeof (val) - 1, 0)) + { + val[sizeof (val) - 1] = 0; + grub_console_terminfo_output.size.y + = (grub_uint8_t) grub_strtoul (val, 0, 10); + } + } + + /* Bogus default value on SLOF in QEMU. */ + if (grub_console_terminfo_output.size.x == 200 + && grub_console_terminfo_output.size.y == 200) + { + grub_console_terminfo_output.size.x = 80; + grub_console_terminfo_output.size.y = 24; + } + + /* Use a small console by default. */ + if (! grub_console_terminfo_output.size.x) + grub_console_terminfo_output.size.x = 80; + if (! grub_console_terminfo_output.size.y) + grub_console_terminfo_output.size.y = 24; +} + +static void +grub_console_setcursor (struct grub_term_output *term, + int on) +{ + grub_terminfo_setcursor (term, on); + + if (!grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_HAS_CURSORONOFF)) + return; + + /* Understood by the Open Firmware flavour in OLPC. */ + if (on) + grub_ieee1275_interpret ("cursor-on", 0); + else + grub_ieee1275_interpret ("cursor-off", 0); +} + +static grub_err_t +grub_console_init_input (struct grub_term_input *term) +{ + grub_ssize_t actual; + + if (grub_ieee1275_get_integer_property (grub_ieee1275_chosen, "stdin", &stdin_ihandle, + sizeof stdin_ihandle, &actual) + || actual != sizeof stdin_ihandle) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot find stdin"); + + return grub_terminfo_input_init (term); +} + +static grub_err_t +grub_console_init_output (struct grub_term_output *term) +{ + grub_ssize_t actual; + + /* The latest PowerMacs don't actually initialize the screen for us, so we + * use this trick to re-open the output device (but we avoid doing this on + * platforms where it's known to be broken). */ + if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_OUTPUT)) + grub_ieee1275_interpret ("output-device output", 0); + + if (grub_ieee1275_get_integer_property (grub_ieee1275_chosen, "stdout", &stdout_ihandle, + sizeof stdout_ihandle, &actual) + || actual != sizeof stdout_ihandle) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot find stdout"); + + /* Initialize colors. */ + if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CANNOT_SET_COLORS)) + { + unsigned col; + for (col = 0; col < ARRAY_SIZE (colors); col++) + grub_ieee1275_set_color (stdout_ihandle, col, colors[col].red, + colors[col].green, colors[col].blue); + + /* Set the right fg and bg colors. */ + grub_terminfo_setcolorstate (term, GRUB_TERM_COLOR_NORMAL); + } + + grub_console_dimensions (); + + grub_terminfo_output_init (term); + + return 0; +} + + + +struct grub_terminfo_input_state grub_console_terminfo_input = + { + .readkey = readkey + }; + +struct grub_terminfo_output_state grub_console_terminfo_output = + { + .put = put, + .size = { 80, 24 } + }; + +static struct grub_term_input grub_console_term_input = + { + .name = "console", + .init = grub_console_init_input, + .getkey = grub_terminfo_getkey, + .data = &grub_console_terminfo_input + }; + +static struct grub_term_output grub_console_term_output = + { + .name = "console", + .init = grub_console_init_output, + .putchar = grub_terminfo_putchar, + .getxy = grub_terminfo_getxy, + .getwh = grub_terminfo_getwh, + .gotoxy = grub_terminfo_gotoxy, + .cls = grub_terminfo_cls, + .setcolorstate = grub_terminfo_setcolorstate, + .setcursor = grub_console_setcursor, + .flags = GRUB_TERM_CODE_TYPE_ASCII, + .data = &grub_console_terminfo_output, + .progress_update_divisor = GRUB_PROGRESS_FAST + }; + +void +grub_console_init_early (void) +{ + grub_term_register_input ("console", &grub_console_term_input); + grub_term_register_output ("console", &grub_console_term_output); +} + +void +grub_console_init_lately (void) +{ + const char *type; + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_ANSI)) + type = "dumb"; + else if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CURSORONOFF_ANSI_BROKEN)) + type = "ieee1275-nocursor"; + else + type = "ieee1275"; + grub_terminfo_init (); + grub_terminfo_output_register (&grub_console_term_output, type); +} + +void +grub_console_fini (void) +{ +} diff --git a/grub-core/term/ieee1275/escc.c b/grub-core/term/ieee1275/escc.c new file mode 100644 index 0000000..e605ff2 --- /dev/null +++ b/grub-core/term/ieee1275/escc.c @@ -0,0 +1,319 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2012 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct grub_escc_descriptor +{ + volatile grub_uint8_t *escc_ctrl; + volatile grub_uint8_t *escc_data; +}; + +static void +do_real_config (struct grub_serial_port *port) +{ + grub_uint8_t bitsspec; + grub_uint8_t parity_stop_spec; + if (port->configured) + return; + + /* Make sure the port is waiting for address now. */ + (void) *port->escc_desc->escc_ctrl; + switch (port->config.speed) + { + case 57600: + *port->escc_desc->escc_ctrl = 13; + *port->escc_desc->escc_ctrl = 0; + *port->escc_desc->escc_ctrl = 12; + *port->escc_desc->escc_ctrl = 0; + *port->escc_desc->escc_ctrl = 14; + *port->escc_desc->escc_ctrl = 1; + *port->escc_desc->escc_ctrl = 11; + *port->escc_desc->escc_ctrl = 0x50; + break; + case 38400: + *port->escc_desc->escc_ctrl = 13; + *port->escc_desc->escc_ctrl = 0; + *port->escc_desc->escc_ctrl = 12; + *port->escc_desc->escc_ctrl = 1; + *port->escc_desc->escc_ctrl = 14; + *port->escc_desc->escc_ctrl = 1; + *port->escc_desc->escc_ctrl = 11; + *port->escc_desc->escc_ctrl = 0x50; + break; + } + + parity_stop_spec = 0; + switch (port->config.parity) + { + case GRUB_SERIAL_PARITY_NONE: + parity_stop_spec |= 0; + break; + case GRUB_SERIAL_PARITY_ODD: + parity_stop_spec |= 1; + break; + case GRUB_SERIAL_PARITY_EVEN: + parity_stop_spec |= 3; + break; + } + + switch (port->config.stop_bits) + { + case GRUB_SERIAL_STOP_BITS_1: + parity_stop_spec |= 0x4; + break; + case GRUB_SERIAL_STOP_BITS_1_5: + parity_stop_spec |= 0x8; + break; + case GRUB_SERIAL_STOP_BITS_2: + parity_stop_spec |= 0xc; + break; + } + + *port->escc_desc->escc_ctrl = 4; + *port->escc_desc->escc_ctrl = 0x40 | parity_stop_spec; + + bitsspec = port->config.word_len - 5; + bitsspec = ((bitsspec >> 1) | (bitsspec << 1)) & 3; + + *port->escc_desc->escc_ctrl = 3; + *port->escc_desc->escc_ctrl = (bitsspec << 6) | 0x1; + + port->configured = 1; + + return; +} + +/* Fetch a key. */ +static int +serial_hw_fetch (struct grub_serial_port *port) +{ + do_real_config (port); + + *port->escc_desc->escc_ctrl = 0; + if (*port->escc_desc->escc_ctrl & 1) + return *port->escc_desc->escc_data; + return -1; +} + +/* Put a character. */ +static void +serial_hw_put (struct grub_serial_port *port, const int c) +{ + grub_uint64_t endtime; + + do_real_config (port); + + if (port->broken > 5) + endtime = grub_get_time_ms (); + else if (port->broken > 1) + endtime = grub_get_time_ms () + 50; + else + endtime = grub_get_time_ms () + 200; + /* Wait until the transmitter holding register is empty. */ + while (1) + { + *port->escc_desc->escc_ctrl = 0; + if (*port->escc_desc->escc_ctrl & 4) + break; + if (grub_get_time_ms () > endtime) + { + port->broken++; + /* There is something wrong. But what can I do? */ + return; + } + } + + if (port->broken) + port->broken--; + + *port->escc_desc->escc_data = c; +} + +/* Initialize a serial device. PORT is the port number for a serial device. + SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600, + 19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used + for the device. Likewise, PARITY is the type of the parity and + STOP_BIT_LEN is the length of the stop bit. The possible values for + WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as + macros. */ +static grub_err_t +serial_hw_configure (struct grub_serial_port *port __attribute__ ((unused)), + struct grub_serial_config *config __attribute__ ((unused))) +{ + if (config->speed != 38400 && config->speed != 57600) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port speed")); + + if (config->parity != GRUB_SERIAL_PARITY_NONE + && config->parity != GRUB_SERIAL_PARITY_ODD + && config->parity != GRUB_SERIAL_PARITY_EVEN) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port parity")); + + if (config->stop_bits != GRUB_SERIAL_STOP_BITS_1 + && config->stop_bits != GRUB_SERIAL_STOP_BITS_1_5 + && config->stop_bits != GRUB_SERIAL_STOP_BITS_2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port stop bits number")); + + if (config->word_len < 5 || config->word_len > 8) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port word length")); + + port->config = *config; + port->configured = 0; + + /* FIXME: should check if the serial terminal was found. */ + + return GRUB_ERR_NONE; +} + +struct grub_serial_driver grub_escc_driver = + { + .configure = serial_hw_configure, + .fetch = serial_hw_fetch, + .put = serial_hw_put + }; + +static struct grub_escc_descriptor escc_descs[2]; +static char *macio = 0; + +static void +add_device (grub_addr_t addr, int channel) +{ + struct grub_serial_port *port; + grub_err_t err; + struct grub_serial_config config = + { + .speed = 38400, + .word_len = 8, + .parity = GRUB_SERIAL_PARITY_NONE, + .stop_bits = GRUB_SERIAL_STOP_BITS_1 + }; + + escc_descs[channel].escc_ctrl + = (volatile grub_uint8_t *) (grub_addr_t) addr; + escc_descs[channel].escc_data = escc_descs[channel].escc_ctrl + 16; + + port = grub_zalloc (sizeof (*port)); + if (!port) + { + grub_errno = 0; + return; + } + + port->name = grub_xasprintf ("escc-ch-%c", channel + 'a'); + if (!port->name) + { + grub_errno = 0; + return; + } + + port->escc_desc = &escc_descs[channel]; + + port->driver = &grub_escc_driver; + + err = port->driver->configure (port, &config); + if (err) + grub_print_error (); + + grub_serial_register (port); +} + +static int +find_macio (struct grub_ieee1275_devalias *alias) +{ + if (grub_strcmp (alias->type, "mac-io") != 0) + return 0; + macio = grub_strdup (alias->path); + return 1; +} + +GRUB_MOD_INIT (escc) +{ + grub_uint32_t macio_addr[4]; + grub_uint32_t escc_addr[2]; + grub_ieee1275_phandle_t dev; + struct grub_ieee1275_devalias alias; + char *escc = 0; + + grub_ieee1275_devices_iterate (find_macio); + if (!macio) + return; + + FOR_IEEE1275_DEVCHILDREN(macio, alias) + if (grub_strcmp (alias.type, "escc") == 0) + { + escc = grub_strdup (alias.path); + break; + } + grub_ieee1275_devalias_free (&alias); + if (!escc) + { + grub_free (macio); + return; + } + + if (grub_ieee1275_finddevice (macio, &dev)) + { + grub_free (macio); + grub_free (escc); + return; + } + if (grub_ieee1275_get_integer_property (dev, "assigned-addresses", + macio_addr, sizeof (macio_addr), 0)) + { + grub_free (macio); + grub_free (escc); + return; + } + + if (grub_ieee1275_finddevice (escc, &dev)) + { + grub_free (macio); + grub_free (escc); + return; + } + + if (grub_ieee1275_get_integer_property (dev, "reg", + escc_addr, sizeof (escc_addr), 0)) + { + grub_free (macio); + grub_free (escc); + return; + } + + add_device (macio_addr[2] + escc_addr[0] + 32, 0); + add_device (macio_addr[2] + escc_addr[0], 1); + + grub_free (macio); + grub_free (escc); +} + +GRUB_MOD_FINI (escc) +{ +} diff --git a/grub-core/term/ieee1275/serial.c b/grub-core/term/ieee1275/serial.c new file mode 100644 index 0000000..9e71ca4 --- /dev/null +++ b/grub-core/term/ieee1275/serial.c @@ -0,0 +1,287 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2012 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IEEE1275_IHANDLE_INVALID ((grub_ieee1275_cell_t) 0) + +struct ofserial_hash_ent +{ + char *devpath; + /* Pointer to shortest available name on nodes representing canonical names, + otherwise NULL. */ + const char *shortest; + struct ofserial_hash_ent *next; +}; + +static void +do_real_config (struct grub_serial_port *port) +{ + if (port->configured) + return; + + if (grub_ieee1275_open (port->elem->devpath, &port->handle) + || port->handle == (grub_ieee1275_ihandle_t) -1) + port->handle = IEEE1275_IHANDLE_INVALID; + + port->configured = 1; +} + +/* Fetch a key. */ +static int +serial_hw_fetch (struct grub_serial_port *port) +{ + grub_ssize_t actual; + char c; + + do_real_config (port); + + if (port->handle == IEEE1275_IHANDLE_INVALID) + return -1; + grub_ieee1275_read (port->handle, &c, 1, &actual); + + if (actual <= 0) + return -1; + return c; +} + +/* Put a character. */ +static void +serial_hw_put (struct grub_serial_port *port, const int c) +{ + grub_ssize_t actual; + char c0 = c; + + do_real_config (port); + + if (port->handle == IEEE1275_IHANDLE_INVALID) + return; + + grub_ieee1275_write (port->handle, &c0, 1, &actual); +} + +/* Initialize a serial device. PORT is the port number for a serial device. + SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600, + 19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used + for the device. Likewise, PARITY is the type of the parity and + STOP_BIT_LEN is the length of the stop bit. The possible values for + WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as + macros. */ +static grub_err_t +serial_hw_configure (struct grub_serial_port *port __attribute__ ((unused)), + struct grub_serial_config *config __attribute__ ((unused))) +{ + /* FIXME: no IEEE1275 serial config available. */ + + return GRUB_ERR_NONE; +} + +struct grub_serial_driver grub_ofserial_driver = + { + .configure = serial_hw_configure, + .fetch = serial_hw_fetch, + .put = serial_hw_put + }; + +#define OFSERIAL_HASH_SZ 8 +static struct ofserial_hash_ent *ofserial_hash[OFSERIAL_HASH_SZ]; + +static int +ofserial_hash_fn (const char *devpath) +{ + int hash = 0; + while (*devpath) + hash ^= *devpath++; + return (hash & (OFSERIAL_HASH_SZ - 1)); +} + +static struct ofserial_hash_ent * +ofserial_hash_find (const char *devpath) +{ + struct ofserial_hash_ent *p = ofserial_hash[ofserial_hash_fn(devpath)]; + + while (p) + { + if (!grub_strcmp (p->devpath, devpath)) + break; + p = p->next; + } + return p; +} + +static struct ofserial_hash_ent * +ofserial_hash_add_real (char *devpath) +{ + struct ofserial_hash_ent *p; + struct ofserial_hash_ent **head = &ofserial_hash[ofserial_hash_fn(devpath)]; + + p = grub_malloc(sizeof (*p)); + if (!p) + return NULL; + + p->devpath = devpath; + p->next = *head; + p->shortest = 0; + *head = p; + return p; +} + +static struct ofserial_hash_ent * +ofserial_hash_add (char *devpath, char *curcan) +{ + struct ofserial_hash_ent *p, *pcan; + + p = ofserial_hash_add_real (devpath); + + grub_dprintf ("serial", "devpath = %s, canonical = %s\n", devpath, curcan); + + if (!curcan) + { + p->shortest = devpath; + return p; + } + + pcan = ofserial_hash_find (curcan); + if (!pcan) + pcan = ofserial_hash_add_real (curcan); + else + grub_free (curcan); + + if (!pcan) + grub_errno = GRUB_ERR_NONE; + else + { + if (!pcan->shortest + || grub_strlen (pcan->shortest) > grub_strlen (devpath)) + pcan->shortest = devpath; + } + + return p; +} + +static void +dev_iterate_real (struct grub_ieee1275_devalias *alias, + int use_name) +{ + struct ofserial_hash_ent *op; + + if (grub_strcmp (alias->type, "serial") != 0) + return; + + grub_dprintf ("serial", "serial name = %s, path = %s\n", alias->name, + alias->path); + + op = ofserial_hash_find (alias->path); + if (!op) + { + char *name = grub_strdup (use_name ? alias->name : alias->path); + char *can = grub_strdup (alias->path); + if (!name || !can) + { + grub_errno = GRUB_ERR_NONE; + grub_free (name); + grub_free (can); + return; + } + op = ofserial_hash_add (name, can); + } + return; +} + +static int +dev_iterate (struct grub_ieee1275_devalias *alias) +{ + dev_iterate_real (alias, 0); + return 0; +} + +static const char * +add_port (struct ofserial_hash_ent *ent) +{ + struct grub_serial_port *port; + char *ptr; + grub_err_t err; + + if (!ent->shortest) + return NULL; + + port = grub_zalloc (sizeof (*port)); + if (!port) + return NULL; + port->name = grub_malloc (sizeof ("ieee1275/") + + grub_strlen (ent->shortest)); + port->elem = ent; + if (!port->name) + return NULL; + ptr = grub_stpcpy (port->name, "ieee1275/"); + grub_strcpy (ptr, ent->shortest); + + port->driver = &grub_ofserial_driver; + err = grub_serial_config_defaults (port); + if (err) + grub_print_error (); + + grub_serial_register (port); + + return port->name; +} + +const char * +grub_ofserial_add_port (const char *path) +{ + struct ofserial_hash_ent *ent; + char *name = grub_strdup (path); + char *can = grub_strdup (path); + + if (!name || ! can) + { + grub_free (name); + grub_free (can); + return NULL; + } + + ent = ofserial_hash_add (name, can); + return add_port (ent); +} + +void +grub_ofserial_init (void) +{ + unsigned i; + struct grub_ieee1275_devalias alias; + + FOR_IEEE1275_DEVALIASES(alias) + dev_iterate_real (&alias, 1); + + grub_ieee1275_devices_iterate (dev_iterate); + + for (i = 0; i < ARRAY_SIZE (ofserial_hash); i++) + { + struct ofserial_hash_ent *ent; + for (ent = ofserial_hash[i]; ent; ent = ent->next) + add_port (ent); + } +} + diff --git a/grub-core/term/morse.c b/grub-core/term/morse.c new file mode 100644 index 0000000..13b8e86 --- /dev/null +++ b/grub-core/term/morse.c @@ -0,0 +1,133 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011,2012,2013 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define BASE_TIME 250 +#define DIH 1 +#define DAH 3 +#define END 0 + +static const char codes[0x80][6] = + { + ['0'] = { DAH, DAH, DAH, DAH, DAH, END }, + ['1'] = { DIH, DAH, DAH, DAH, DAH, END }, + ['2'] = { DIH, DIH, DAH, DAH, DAH, END }, + ['3'] = { DIH, DIH, DIH, DAH, DAH, END }, + ['4'] = { DIH, DIH, DIH, DIH, DAH, END }, + ['5'] = { DIH, DIH, DIH, DIH, DIH, END }, + ['6'] = { DAH, DIH, DIH, DIH, DIH, END }, + ['7'] = { DAH, DAH, DIH, DIH, DIH, END }, + ['8'] = { DAH, DAH, DAH, DIH, DIH, END }, + ['9'] = { DAH, DAH, DAH, DAH, DIH, END }, + ['a'] = { DIH, DAH, END }, + ['b'] = { DAH, DIH, DIH, DIH, END }, + ['c'] = { DAH, DIH, DAH, DIH, END }, + ['d'] = { DAH, DIH, DIH, END }, + ['e'] = { DIH, END }, + ['f'] = { DIH, DIH, DAH, DIH, END }, + ['g'] = { DAH, DAH, DIH, END }, + ['h'] = { DIH, DIH, DIH, DIH, END }, + ['i'] = { DIH, DIH, END }, + ['j'] = { DIH, DAH, DAH, DAH, END }, + ['k'] = { DAH, DIH, DAH, END }, + ['l'] = { DIH, DAH, DIH, DIH, END }, + ['m'] = { DAH, DAH, END }, + ['n'] = { DAH, DIH, END }, + ['o'] = { DAH, DAH, DAH, END }, + ['p'] = { DIH, DAH, DAH, DIH, END }, + ['q'] = { DAH, DAH, DIH, DAH, END }, + ['r'] = { DIH, DAH, DIH, END }, + ['s'] = { DIH, DIH, DIH, END }, + ['t'] = { DAH, END }, + ['u'] = { DIH, DIH, DAH, END }, + ['v'] = { DIH, DIH, DIH, DAH, END }, + ['w'] = { DIH, DAH, DAH, END }, + ['x'] = { DAH, DIH, DIH, DAH, END }, + ['y'] = { DAH, DIH, DAH, DAH, END }, + ['z'] = { DAH, DAH, DIH, DIH, END } + }; + +static void +grub_audio_tone (int length) +{ + grub_speaker_beep_on (1000); + grub_millisleep (length); + grub_speaker_beep_off (); +} + +static void +grub_audio_putchar (struct grub_term_output *term __attribute__ ((unused)), + const struct grub_unicode_glyph *c_in) +{ + grub_uint8_t c; + int i; + + /* For now, do not try to use a surrogate pair. */ + if (c_in->base > 0x7f) + c = '?'; + else + c = grub_tolower (c_in->base); + for (i = 0; codes[c][i]; i++) + { + grub_audio_tone (codes[c][i] * BASE_TIME); + grub_millisleep (BASE_TIME); + } + grub_millisleep (2 * BASE_TIME); +} + + +static int +dummy (void) +{ + return 0; +} + +static struct grub_term_output grub_audio_term_output = + { + .name = "morse", + .init = (void *) dummy, + .fini = (void *) dummy, + .putchar = grub_audio_putchar, + .getwh = (void *) dummy, + .getxy = (void *) dummy, + .gotoxy = (void *) dummy, + .cls = (void *) dummy, + .setcolorstate = (void *) dummy, + .setcursor = (void *) dummy, + .flags = GRUB_TERM_CODE_TYPE_ASCII | GRUB_TERM_DUMB, + .progress_update_divisor = GRUB_PROGRESS_NO_UPDATE + }; + +GRUB_MOD_INIT (morse) +{ + grub_term_register_output ("audio", &grub_audio_term_output); +} + +GRUB_MOD_FINI (morse) +{ + grub_term_unregister_output (&grub_audio_term_output); +} diff --git a/grub-core/term/ns8250.c b/grub-core/term/ns8250.c new file mode 100644 index 0000000..39809d0 --- /dev/null +++ b/grub-core/term/ns8250.c @@ -0,0 +1,318 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GRUB_MACHINE_PCBIOS +#include +static const unsigned short *serial_hw_io_addr = (const unsigned short *) GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR; +#define GRUB_SERIAL_PORT_NUM 4 +#else +#include +static const grub_port_t serial_hw_io_addr[] = GRUB_MACHINE_SERIAL_PORTS; +#define GRUB_SERIAL_PORT_NUM (ARRAY_SIZE(serial_hw_io_addr)) +#endif + +static int dead_ports = 0; + +#ifdef GRUB_MACHINE_MIPS_LOONGSON +#define DEFAULT_BASE_CLOCK (2 * 115200) +#else +#define DEFAULT_BASE_CLOCK 115200 +#endif + + +/* Convert speed to divisor. */ +static unsigned short +serial_get_divisor (const struct grub_serial_port *port __attribute__ ((unused)), + const struct grub_serial_config *config) +{ + grub_uint32_t base_clock; + grub_uint32_t divisor; + grub_uint32_t actual_speed, error; + + base_clock = config->base_clock ? (config->base_clock >> 4) : DEFAULT_BASE_CLOCK; + + divisor = (base_clock + (config->speed / 2)) / config->speed; + if (config->speed == 0) + return 0; + if (divisor > 0xffff || divisor == 0) + return 0; + actual_speed = base_clock / divisor; + error = actual_speed > config->speed ? (actual_speed - config->speed) + : (config->speed - actual_speed); + if (error > (config->speed / 30 + 1)) + return 0; + return divisor; +} + +static void +do_real_config (struct grub_serial_port *port) +{ + int divisor; + unsigned char status = 0; + grub_uint64_t endtime; + + const unsigned char parities[] = { + [GRUB_SERIAL_PARITY_NONE] = UART_NO_PARITY, + [GRUB_SERIAL_PARITY_ODD] = UART_ODD_PARITY, + [GRUB_SERIAL_PARITY_EVEN] = UART_EVEN_PARITY + }; + const unsigned char stop_bits[] = { + [GRUB_SERIAL_STOP_BITS_1] = UART_1_STOP_BIT, + [GRUB_SERIAL_STOP_BITS_2] = UART_2_STOP_BITS, + }; + + if (port->configured) + return; + + port->broken = 0; + + divisor = serial_get_divisor (port, &port->config); + + /* Turn off the interrupt. */ + grub_outb (0, port->port + UART_IER); + + /* Set DLAB. */ + grub_outb (UART_DLAB, port->port + UART_LCR); + + /* Set the baud rate. */ + grub_outb (divisor & 0xFF, port->port + UART_DLL); + grub_outb (divisor >> 8, port->port + UART_DLH); + + /* Set the line status. */ + status |= (parities[port->config.parity] + | (port->config.word_len - 5) + | stop_bits[port->config.stop_bits]); + grub_outb (status, port->port + UART_LCR); + + if (port->config.rtscts) + { + /* Enable the FIFO. */ + grub_outb (UART_ENABLE_FIFO_TRIGGER1, port->port + UART_FCR); + + /* Turn on DTR and RTS. */ + grub_outb (UART_ENABLE_DTRRTS, port->port + UART_MCR); + } + else + { + /* Enable the FIFO. */ + grub_outb (UART_ENABLE_FIFO_TRIGGER14, port->port + UART_FCR); + + /* Turn on DTR, RTS, and OUT2. */ + grub_outb (UART_ENABLE_DTRRTS | UART_ENABLE_OUT2, port->port + UART_MCR); + } + + /* Drain the input buffer. */ + endtime = grub_get_time_ms () + 1000; + while (grub_inb (port->port + UART_LSR) & UART_DATA_READY) + { + grub_inb (port->port + UART_RX); + if (grub_get_time_ms () > endtime) + { + port->broken = 1; + break; + } + } + + port->configured = 1; +} + +/* Fetch a key. */ +static int +serial_hw_fetch (struct grub_serial_port *port) +{ + do_real_config (port); + if (grub_inb (port->port + UART_LSR) & UART_DATA_READY) + return grub_inb (port->port + UART_RX); + + return -1; +} + +/* Put a character. */ +static void +serial_hw_put (struct grub_serial_port *port, const int c) +{ + grub_uint64_t endtime; + + do_real_config (port); + + if (port->broken > 5) + endtime = grub_get_time_ms (); + else if (port->broken > 1) + endtime = grub_get_time_ms () + 50; + else + endtime = grub_get_time_ms () + 200; + /* Wait until the transmitter holding register is empty. */ + while ((grub_inb (port->port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0) + { + if (grub_get_time_ms () > endtime) + { + port->broken++; + /* There is something wrong. But what can I do? */ + return; + } + } + + if (port->broken) + port->broken--; + + grub_outb (c, port->port + UART_TX); +} + +/* Initialize a serial device. PORT is the port number for a serial device. + SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600, + 19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used + for the device. Likewise, PARITY is the type of the parity and + STOP_BIT_LEN is the length of the stop bit. The possible values for + WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as + macros. */ +static grub_err_t +serial_hw_configure (struct grub_serial_port *port, + struct grub_serial_config *config) +{ + unsigned short divisor; + + divisor = serial_get_divisor (port, config); + if (divisor == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port speed")); + + if (config->parity != GRUB_SERIAL_PARITY_NONE + && config->parity != GRUB_SERIAL_PARITY_ODD + && config->parity != GRUB_SERIAL_PARITY_EVEN) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port parity")); + + if (config->stop_bits != GRUB_SERIAL_STOP_BITS_1 + && config->stop_bits != GRUB_SERIAL_STOP_BITS_2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port stop bits number")); + + if (config->word_len < 5 || config->word_len > 8) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port word length")); + + port->config = *config; + port->configured = 0; + + /* FIXME: should check if the serial terminal was found. */ + + return GRUB_ERR_NONE; +} + +struct grub_serial_driver grub_ns8250_driver = + { + .configure = serial_hw_configure, + .fetch = serial_hw_fetch, + .put = serial_hw_put + }; + +static char com_names[GRUB_SERIAL_PORT_NUM][20]; +static struct grub_serial_port com_ports[GRUB_SERIAL_PORT_NUM]; + +void +grub_ns8250_init (void) +{ + unsigned i; + for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++) + if (serial_hw_io_addr[i]) + { + grub_err_t err; + + grub_outb (0x5a, serial_hw_io_addr[i] + UART_SR); + if (grub_inb (serial_hw_io_addr[i] + UART_SR) != 0x5a) + { + dead_ports |= (1 << i); + continue; + } + grub_outb (0xa5, serial_hw_io_addr[i] + UART_SR); + if (grub_inb (serial_hw_io_addr[i] + UART_SR) != 0xa5) + { + dead_ports |= (1 << i); + continue; + } + + grub_snprintf (com_names[i], sizeof (com_names[i]), "com%d", i); + com_ports[i].name = com_names[i]; + com_ports[i].driver = &grub_ns8250_driver; + com_ports[i].port = serial_hw_io_addr[i]; + err = grub_serial_config_defaults (&com_ports[i]); + if (err) + grub_print_error (); + + grub_serial_register (&com_ports[i]); + } +} + +/* Return the port number for the UNITth serial device. */ +grub_port_t +grub_ns8250_hw_get_port (const unsigned int unit) +{ + if (unit < GRUB_SERIAL_PORT_NUM + && !(dead_ports & (1 << unit))) + return serial_hw_io_addr[unit]; + else + return 0; +} + +char * +grub_serial_ns8250_add_port (grub_port_t port) +{ + struct grub_serial_port *p; + unsigned i; + for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++) + if (com_ports[i].port == port) + { + if (dead_ports & (1 << i)) + return NULL; + return com_names[i]; + } + + grub_outb (0x5a, port + UART_SR); + if (grub_inb (port + UART_SR) != 0x5a) + return NULL; + + grub_outb (0xa5, port + UART_SR); + if (grub_inb (port + UART_SR) != 0xa5) + return NULL; + + p = grub_malloc (sizeof (*p)); + if (!p) + return NULL; + p->name = grub_xasprintf ("port%lx", (unsigned long) port); + if (!p->name) + { + grub_free (p); + return NULL; + } + p->driver = &grub_ns8250_driver; + grub_serial_config_defaults (p); + p->port = port; + grub_serial_register (p); + + return p->name; +} diff --git a/grub-core/term/ps2.c b/grub-core/term/ps2.c new file mode 100644 index 0000000..7ae4e9f --- /dev/null +++ b/grub-core/term/ps2.c @@ -0,0 +1,387 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include + +#define KEYBOARD_LED_SCROLL (1 << 0) +#define KEYBOARD_LED_NUM (1 << 1) +#define KEYBOARD_LED_CAPS (1 << 2) + +static const grub_uint8_t set1_mapping[128] = + { + /* 0x00 */ 0 /* Unused */, GRUB_KEYBOARD_KEY_ESCAPE, + /* 0x02 */ GRUB_KEYBOARD_KEY_1, GRUB_KEYBOARD_KEY_2, + /* 0x04 */ GRUB_KEYBOARD_KEY_3, GRUB_KEYBOARD_KEY_4, + /* 0x06 */ GRUB_KEYBOARD_KEY_5, GRUB_KEYBOARD_KEY_6, + /* 0x08 */ GRUB_KEYBOARD_KEY_7, GRUB_KEYBOARD_KEY_8, + /* 0x0a */ GRUB_KEYBOARD_KEY_9, GRUB_KEYBOARD_KEY_0, + /* 0x0c */ GRUB_KEYBOARD_KEY_DASH, GRUB_KEYBOARD_KEY_EQUAL, + /* 0x0e */ GRUB_KEYBOARD_KEY_BACKSPACE, GRUB_KEYBOARD_KEY_TAB, + /* 0x10 */ GRUB_KEYBOARD_KEY_Q, GRUB_KEYBOARD_KEY_W, + /* 0x12 */ GRUB_KEYBOARD_KEY_E, GRUB_KEYBOARD_KEY_R, + /* 0x14 */ GRUB_KEYBOARD_KEY_T, GRUB_KEYBOARD_KEY_Y, + /* 0x16 */ GRUB_KEYBOARD_KEY_U, GRUB_KEYBOARD_KEY_I, + /* 0x18 */ GRUB_KEYBOARD_KEY_O, GRUB_KEYBOARD_KEY_P, + /* 0x1a */ GRUB_KEYBOARD_KEY_LBRACKET, GRUB_KEYBOARD_KEY_RBRACKET, + /* 0x1c */ GRUB_KEYBOARD_KEY_ENTER, GRUB_KEYBOARD_KEY_LEFT_CTRL, + /* 0x1e */ GRUB_KEYBOARD_KEY_A, GRUB_KEYBOARD_KEY_S, + /* 0x20 */ GRUB_KEYBOARD_KEY_D, GRUB_KEYBOARD_KEY_F, + /* 0x22 */ GRUB_KEYBOARD_KEY_G, GRUB_KEYBOARD_KEY_H, + /* 0x24 */ GRUB_KEYBOARD_KEY_J, GRUB_KEYBOARD_KEY_K, + /* 0x26 */ GRUB_KEYBOARD_KEY_L, GRUB_KEYBOARD_KEY_SEMICOLON, + /* 0x28 */ GRUB_KEYBOARD_KEY_DQUOTE, GRUB_KEYBOARD_KEY_RQUOTE, + /* 0x2a */ GRUB_KEYBOARD_KEY_LEFT_SHIFT, GRUB_KEYBOARD_KEY_BACKSLASH, + /* 0x2c */ GRUB_KEYBOARD_KEY_Z, GRUB_KEYBOARD_KEY_X, + /* 0x2e */ GRUB_KEYBOARD_KEY_C, GRUB_KEYBOARD_KEY_V, + /* 0x30 */ GRUB_KEYBOARD_KEY_B, GRUB_KEYBOARD_KEY_N, + /* 0x32 */ GRUB_KEYBOARD_KEY_M, GRUB_KEYBOARD_KEY_COMMA, + /* 0x34 */ GRUB_KEYBOARD_KEY_DOT, GRUB_KEYBOARD_KEY_SLASH, + /* 0x36 */ GRUB_KEYBOARD_KEY_RIGHT_SHIFT, GRUB_KEYBOARD_KEY_NUMMUL, + /* 0x38 */ GRUB_KEYBOARD_KEY_LEFT_ALT, GRUB_KEYBOARD_KEY_SPACE, + /* 0x3a */ GRUB_KEYBOARD_KEY_CAPS_LOCK, GRUB_KEYBOARD_KEY_F1, + /* 0x3c */ GRUB_KEYBOARD_KEY_F2, GRUB_KEYBOARD_KEY_F3, + /* 0x3e */ GRUB_KEYBOARD_KEY_F4, GRUB_KEYBOARD_KEY_F5, + /* 0x40 */ GRUB_KEYBOARD_KEY_F6, GRUB_KEYBOARD_KEY_F7, + /* 0x42 */ GRUB_KEYBOARD_KEY_F8, GRUB_KEYBOARD_KEY_F9, + /* 0x44 */ GRUB_KEYBOARD_KEY_F10, GRUB_KEYBOARD_KEY_NUM_LOCK, + /* 0x46 */ GRUB_KEYBOARD_KEY_SCROLL_LOCK, GRUB_KEYBOARD_KEY_NUM7, + /* 0x48 */ GRUB_KEYBOARD_KEY_NUM8, GRUB_KEYBOARD_KEY_NUM9, + /* 0x4a */ GRUB_KEYBOARD_KEY_NUMMINUS, GRUB_KEYBOARD_KEY_NUM4, + /* 0x4c */ GRUB_KEYBOARD_KEY_NUM5, GRUB_KEYBOARD_KEY_NUM6, + /* 0x4e */ GRUB_KEYBOARD_KEY_NUMPLUS, GRUB_KEYBOARD_KEY_NUM1, + /* 0x50 */ GRUB_KEYBOARD_KEY_NUM2, GRUB_KEYBOARD_KEY_NUM3, + /* 0x52 */ GRUB_KEYBOARD_KEY_NUM0, GRUB_KEYBOARD_KEY_NUMDOT, + /* 0x54 */ 0, 0, + /* 0x56 */ GRUB_KEYBOARD_KEY_102ND, GRUB_KEYBOARD_KEY_F11, + /* 0x58 */ GRUB_KEYBOARD_KEY_F12, 0, + /* 0x5a */ 0, 0, + /* 0x5c */ 0, 0, + /* 0x5e */ 0, 0, + /* 0x60 */ 0, 0, + /* 0x62 */ 0, 0, + /* OLPC keys. Just mapped to normal keys. */ + /* 0x64 */ 0, GRUB_KEYBOARD_KEY_UP, + /* 0x66 */ GRUB_KEYBOARD_KEY_DOWN, GRUB_KEYBOARD_KEY_LEFT, + /* 0x68 */ GRUB_KEYBOARD_KEY_RIGHT, 0, + /* 0x6a */ 0, 0, + /* 0x6c */ 0, 0, + /* 0x6e */ 0, 0, + /* 0x70 */ 0, 0, + /* 0x72 */ 0, GRUB_KEYBOARD_KEY_JP_RO, + /* 0x74 */ 0, 0, + /* 0x76 */ 0, 0, + /* 0x78 */ 0, 0, + /* 0x7a */ 0, 0, + /* 0x7c */ 0, GRUB_KEYBOARD_KEY_JP_YEN, + /* 0x7e */ GRUB_KEYBOARD_KEY_KPCOMMA + }; + +static const struct +{ + grub_uint8_t from, to; +} set1_e0_mapping[] = + { + {0x1c, GRUB_KEYBOARD_KEY_NUMENTER}, + {0x1d, GRUB_KEYBOARD_KEY_RIGHT_CTRL}, + {0x35, GRUB_KEYBOARD_KEY_NUMSLASH }, + {0x38, GRUB_KEYBOARD_KEY_RIGHT_ALT}, + {0x47, GRUB_KEYBOARD_KEY_HOME}, + {0x48, GRUB_KEYBOARD_KEY_UP}, + {0x49, GRUB_KEYBOARD_KEY_PPAGE}, + {0x4b, GRUB_KEYBOARD_KEY_LEFT}, + {0x4d, GRUB_KEYBOARD_KEY_RIGHT}, + {0x4f, GRUB_KEYBOARD_KEY_END}, + {0x50, GRUB_KEYBOARD_KEY_DOWN}, + {0x51, GRUB_KEYBOARD_KEY_NPAGE}, + {0x52, GRUB_KEYBOARD_KEY_INSERT}, + {0x53, GRUB_KEYBOARD_KEY_DELETE}, + }; + +static const grub_uint8_t set2_mapping[256] = + { + /* 0x00 */ 0, GRUB_KEYBOARD_KEY_F9, + /* 0x02 */ 0, GRUB_KEYBOARD_KEY_F5, + /* 0x04 */ GRUB_KEYBOARD_KEY_F3, GRUB_KEYBOARD_KEY_F1, + /* 0x06 */ GRUB_KEYBOARD_KEY_F2, GRUB_KEYBOARD_KEY_F12, + /* 0x08 */ 0, GRUB_KEYBOARD_KEY_F10, + /* 0x0a */ GRUB_KEYBOARD_KEY_F8, GRUB_KEYBOARD_KEY_F6, + /* 0x0c */ GRUB_KEYBOARD_KEY_F4, GRUB_KEYBOARD_KEY_TAB, + /* 0x0e */ GRUB_KEYBOARD_KEY_RQUOTE, 0, + /* 0x10 */ 0, GRUB_KEYBOARD_KEY_LEFT_ALT, + /* 0x12 */ GRUB_KEYBOARD_KEY_LEFT_SHIFT, 0, + /* 0x14 */ GRUB_KEYBOARD_KEY_LEFT_CTRL, GRUB_KEYBOARD_KEY_Q, + /* 0x16 */ GRUB_KEYBOARD_KEY_1, 0, + /* 0x18 */ 0, 0, + /* 0x1a */ GRUB_KEYBOARD_KEY_Z, GRUB_KEYBOARD_KEY_S, + /* 0x1c */ GRUB_KEYBOARD_KEY_A, GRUB_KEYBOARD_KEY_W, + /* 0x1e */ GRUB_KEYBOARD_KEY_2, 0, + /* 0x20 */ 0, GRUB_KEYBOARD_KEY_C, + /* 0x22 */ GRUB_KEYBOARD_KEY_X, GRUB_KEYBOARD_KEY_D, + /* 0x24 */ GRUB_KEYBOARD_KEY_E, GRUB_KEYBOARD_KEY_4, + /* 0x26 */ GRUB_KEYBOARD_KEY_3, 0, + /* 0x28 */ 0, GRUB_KEYBOARD_KEY_SPACE, + /* 0x2a */ GRUB_KEYBOARD_KEY_V, GRUB_KEYBOARD_KEY_F, + /* 0x2c */ GRUB_KEYBOARD_KEY_T, GRUB_KEYBOARD_KEY_R, + /* 0x2e */ GRUB_KEYBOARD_KEY_5, 0, + /* 0x30 */ 0, GRUB_KEYBOARD_KEY_N, + /* 0x32 */ GRUB_KEYBOARD_KEY_B, GRUB_KEYBOARD_KEY_H, + /* 0x34 */ GRUB_KEYBOARD_KEY_G, GRUB_KEYBOARD_KEY_Y, + /* 0x36 */ GRUB_KEYBOARD_KEY_6, 0, + /* 0x38 */ 0, 0, + /* 0x3a */ GRUB_KEYBOARD_KEY_M, GRUB_KEYBOARD_KEY_J, + /* 0x3c */ GRUB_KEYBOARD_KEY_U, GRUB_KEYBOARD_KEY_7, + /* 0x3e */ GRUB_KEYBOARD_KEY_8, 0, + /* 0x40 */ 0, GRUB_KEYBOARD_KEY_COMMA, + /* 0x42 */ GRUB_KEYBOARD_KEY_K, GRUB_KEYBOARD_KEY_I, + /* 0x44 */ GRUB_KEYBOARD_KEY_O, GRUB_KEYBOARD_KEY_0, + /* 0x46 */ GRUB_KEYBOARD_KEY_9, 0, + /* 0x48 */ 0, GRUB_KEYBOARD_KEY_DOT, + /* 0x4a */ GRUB_KEYBOARD_KEY_SLASH, GRUB_KEYBOARD_KEY_L, + /* 0x4c */ GRUB_KEYBOARD_KEY_SEMICOLON, GRUB_KEYBOARD_KEY_P, + /* 0x4e */ GRUB_KEYBOARD_KEY_DASH, 0, + /* 0x50 */ 0, GRUB_KEYBOARD_KEY_JP_RO, + /* 0x52 */ GRUB_KEYBOARD_KEY_DQUOTE, 0, + /* 0x54 */ GRUB_KEYBOARD_KEY_LBRACKET, GRUB_KEYBOARD_KEY_EQUAL, + /* 0x56 */ 0, 0, + /* 0x58 */ GRUB_KEYBOARD_KEY_CAPS_LOCK, GRUB_KEYBOARD_KEY_RIGHT_SHIFT, + /* 0x5a */ GRUB_KEYBOARD_KEY_ENTER, GRUB_KEYBOARD_KEY_RBRACKET, + /* 0x5c */ 0, GRUB_KEYBOARD_KEY_BACKSLASH, + /* 0x5e */ 0, 0, + /* 0x60 */ 0, GRUB_KEYBOARD_KEY_102ND, + /* 0x62 */ 0, 0, + /* 0x64 */ 0, 0, + /* 0x66 */ GRUB_KEYBOARD_KEY_BACKSPACE, 0, + /* 0x68 */ 0, GRUB_KEYBOARD_KEY_NUM1, + /* 0x6a */ GRUB_KEYBOARD_KEY_JP_YEN, GRUB_KEYBOARD_KEY_NUM4, + /* 0x6c */ GRUB_KEYBOARD_KEY_NUM7, GRUB_KEYBOARD_KEY_KPCOMMA, + /* 0x6e */ 0, 0, + /* 0x70 */ GRUB_KEYBOARD_KEY_NUM0, GRUB_KEYBOARD_KEY_NUMDOT, + /* 0x72 */ GRUB_KEYBOARD_KEY_NUM2, GRUB_KEYBOARD_KEY_NUM5, + /* 0x74 */ GRUB_KEYBOARD_KEY_NUM6, GRUB_KEYBOARD_KEY_NUM8, + /* 0x76 */ GRUB_KEYBOARD_KEY_ESCAPE, GRUB_KEYBOARD_KEY_NUM_LOCK, + /* 0x78 */ GRUB_KEYBOARD_KEY_F11, GRUB_KEYBOARD_KEY_NUMPLUS, + /* 0x7a */ GRUB_KEYBOARD_KEY_NUM3, GRUB_KEYBOARD_KEY_NUMMINUS, + /* 0x7c */ GRUB_KEYBOARD_KEY_NUMMUL, GRUB_KEYBOARD_KEY_NUM9, + /* 0x7e */ GRUB_KEYBOARD_KEY_SCROLL_LOCK, 0, + /* 0x80 */ 0, 0, + /* 0x82 */ 0, GRUB_KEYBOARD_KEY_F7, + }; + +static const struct +{ + grub_uint8_t from, to; +} set2_e0_mapping[] = + { + {0x11, GRUB_KEYBOARD_KEY_RIGHT_ALT}, + {0x14, GRUB_KEYBOARD_KEY_RIGHT_CTRL}, + {0x4a, GRUB_KEYBOARD_KEY_NUMSLASH}, + {0x5a, GRUB_KEYBOARD_KEY_NUMENTER}, + {0x69, GRUB_KEYBOARD_KEY_END}, + {0x6b, GRUB_KEYBOARD_KEY_LEFT}, + {0x6c, GRUB_KEYBOARD_KEY_HOME}, + {0x70, GRUB_KEYBOARD_KEY_INSERT}, + {0x71, GRUB_KEYBOARD_KEY_DELETE}, + {0x72, GRUB_KEYBOARD_KEY_DOWN}, + {0x74, GRUB_KEYBOARD_KEY_RIGHT}, + {0x75, GRUB_KEYBOARD_KEY_UP}, + {0x7a, GRUB_KEYBOARD_KEY_NPAGE}, + {0x7d, GRUB_KEYBOARD_KEY_PPAGE}, + }; + +static int +fetch_key (struct grub_ps2_state *ps2_state, grub_uint8_t at_key, int *is_break) +{ + int was_ext = 0; + int ret = 0; + + /* May happen if no keyboard is connected. Just ignore this. */ + if (at_key == 0xff) + return -1; + if (at_key == 0xe0) + { + ps2_state->e0_received = 1; + return -1; + } + + if ((ps2_state->current_set == 2 || ps2_state->current_set == 3) && at_key == 0xf0) + { + ps2_state->f0_received = 1; + return -1; + } + + /* Setting LEDs may generate ACKs. */ + if (at_key == GRUB_AT_ACK) + return -1; + + was_ext = ps2_state->e0_received; + ps2_state->e0_received = 0; + + switch (ps2_state->current_set) + { + case 1: + *is_break = !!(at_key & 0x80); + if (!was_ext) + ret = set1_mapping[at_key & 0x7f]; + else + { + unsigned i; + for (i = 0; i < ARRAY_SIZE (set1_e0_mapping); i++) + if (set1_e0_mapping[i].from == (at_key & 0x7f)) + { + ret = set1_e0_mapping[i].to; + break; + } + } + break; + case 2: + *is_break = ps2_state->f0_received; + ps2_state->f0_received = 0; + if (!was_ext) + ret = set2_mapping[at_key]; + else + { + unsigned i; + for (i = 0; i < ARRAY_SIZE (set2_e0_mapping); i++) + if (set2_e0_mapping[i].from == at_key) + { + ret = set2_e0_mapping[i].to; + break; + } + } + break; + default: + return -1; + } + if (!ret) + { + if (was_ext) + grub_dprintf ("atkeyb", "Unknown key 0xe0+0x%02x from set %d\n", + at_key, ps2_state->current_set); + else + grub_dprintf ("atkeyb", "Unknown key 0x%02x from set %d\n", + at_key, ps2_state->current_set); + return -1; + } + return ret; +} + +/* FIXME: This should become an interrupt service routine. For now + it's just used to catch events from control keys. */ +static int +grub_keyboard_isr (struct grub_ps2_state *ps2_state, + grub_keyboard_key_t key, int is_break) +{ + if (!is_break) + switch (key) + { + case GRUB_KEYBOARD_KEY_LEFT_SHIFT: + ps2_state->at_keyboard_status |= GRUB_TERM_STATUS_LSHIFT; + return 1; + case GRUB_KEYBOARD_KEY_RIGHT_SHIFT: + ps2_state->at_keyboard_status |= GRUB_TERM_STATUS_RSHIFT; + return 1; + case GRUB_KEYBOARD_KEY_LEFT_CTRL: + ps2_state->at_keyboard_status |= GRUB_TERM_STATUS_LCTRL; + return 1; + case GRUB_KEYBOARD_KEY_RIGHT_CTRL: + ps2_state->at_keyboard_status |= GRUB_TERM_STATUS_RCTRL; + return 1; + case GRUB_KEYBOARD_KEY_RIGHT_ALT: + ps2_state->at_keyboard_status |= GRUB_TERM_STATUS_RALT; + return 1; + case GRUB_KEYBOARD_KEY_LEFT_ALT: + ps2_state->at_keyboard_status |= GRUB_TERM_STATUS_LALT; + return 1; + default: + return 0; + } + else + switch (key) + { + case GRUB_KEYBOARD_KEY_LEFT_SHIFT: + ps2_state->at_keyboard_status &= ~GRUB_TERM_STATUS_LSHIFT; + return 1; + case GRUB_KEYBOARD_KEY_RIGHT_SHIFT: + ps2_state->at_keyboard_status &= ~GRUB_TERM_STATUS_RSHIFT; + return 1; + case GRUB_KEYBOARD_KEY_LEFT_CTRL: + ps2_state->at_keyboard_status &= ~GRUB_TERM_STATUS_LCTRL; + return 1; + case GRUB_KEYBOARD_KEY_RIGHT_CTRL: + ps2_state->at_keyboard_status &= ~GRUB_TERM_STATUS_RCTRL; + return 1; + case GRUB_KEYBOARD_KEY_RIGHT_ALT: + ps2_state->at_keyboard_status &= ~GRUB_TERM_STATUS_RALT; + return 1; + case GRUB_KEYBOARD_KEY_LEFT_ALT: + ps2_state->at_keyboard_status &= ~GRUB_TERM_STATUS_LALT; + return 1; + default: + return 0; + } +} + +/* If there is a key pending, return it; otherwise return GRUB_TERM_NO_KEY. */ +int +grub_ps2_process_incoming_byte (struct grub_ps2_state *ps2_state, + grub_uint8_t at_key) +{ + int code; + int is_break = 0; + + code = fetch_key (ps2_state, at_key, &is_break); + if (code == -1) + return GRUB_TERM_NO_KEY; + + if (grub_keyboard_isr (ps2_state, code, is_break)) + return GRUB_TERM_NO_KEY; + if (is_break) + return GRUB_TERM_NO_KEY; +#ifdef DEBUG_AT_KEYBOARD + grub_dprintf ("atkeyb", "Detected key 0x%x\n", code); +#endif + switch (code) + { + case GRUB_KEYBOARD_KEY_CAPS_LOCK: + ps2_state->at_keyboard_status ^= GRUB_TERM_STATUS_CAPS; + ps2_state->led_status ^= KEYBOARD_LED_CAPS; + +#ifdef DEBUG_AT_KEYBOARD + grub_dprintf ("atkeyb", "caps_lock = %d\n", !!(ps2_state->at_keyboard_status & GRUB_KEYBOARD_STATUS_CAPS_LOCK)); +#endif + return GRUB_TERM_NO_KEY; + case GRUB_KEYBOARD_KEY_NUM_LOCK: + ps2_state->at_keyboard_status ^= GRUB_TERM_STATUS_NUM; + ps2_state->led_status ^= KEYBOARD_LED_NUM; + +#ifdef DEBUG_AT_KEYBOARD + grub_dprintf ("atkeyb", "num_lock = %d\n", !!(ps2_state->at_keyboard_status & GRUB_KEYBOARD_STATUS_NUM_LOCK)); +#endif + return GRUB_TERM_NO_KEY; + case GRUB_KEYBOARD_KEY_SCROLL_LOCK: + ps2_state->at_keyboard_status ^= GRUB_TERM_STATUS_SCROLL; + ps2_state->led_status ^= KEYBOARD_LED_SCROLL; + return GRUB_TERM_NO_KEY; + default: + return grub_term_map_key (code, ps2_state->at_keyboard_status); + } +} diff --git a/grub-core/term/serial.c b/grub-core/term/serial.c new file mode 100644 index 0000000..f9271b0 --- /dev/null +++ b/grub-core/term/serial.c @@ -0,0 +1,463 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#if !defined (GRUB_MACHINE_EMU) && (defined(__mips__) || defined (__i386__) || defined (__x86_64__)) +#include +#endif +#include +#include +#include +#ifdef GRUB_MACHINE_MIPS_LOONGSON +#include +#endif +#ifdef GRUB_MACHINE_IEEE1275 +#include +#endif + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define FOR_SERIAL_PORTS(var) FOR_LIST_ELEMENTS((var), (grub_serial_ports)) + +enum + { + OPTION_UNIT, + OPTION_PORT, + OPTION_SPEED, + OPTION_WORD, + OPTION_PARITY, + OPTION_STOP, + OPTION_BASE_CLOCK, + OPTION_RTSCTS + }; + +/* Argument options. */ +static const struct grub_arg_option options[] = +{ + {"unit", 'u', 0, N_("Set the serial unit."), 0, ARG_TYPE_INT}, + {"port", 'p', 0, N_("Set the serial port address."), 0, ARG_TYPE_STRING}, + {"speed", 's', 0, N_("Set the serial port speed."), 0, ARG_TYPE_INT}, + {"word", 'w', 0, N_("Set the serial port word length."), 0, ARG_TYPE_INT}, + {"parity", 'r', 0, N_("Set the serial port parity."), 0, ARG_TYPE_STRING}, + {"stop", 't', 0, N_("Set the serial port stop bits."), 0, ARG_TYPE_INT}, + {"base-clock", 'b', 0, N_("Set the base frequency."), 0, ARG_TYPE_STRING}, + {"rtscts", 'f', 0, N_("Enable/disable RTS/CTS."), "on|off", ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} +}; + +static struct grub_serial_port *grub_serial_ports; + +struct grub_serial_output_state +{ + struct grub_terminfo_output_state tinfo; + struct grub_serial_port *port; +}; + +struct grub_serial_input_state +{ + struct grub_terminfo_input_state tinfo; + struct grub_serial_port *port; +}; + +static void +serial_put (grub_term_output_t term, const int c) +{ + struct grub_serial_output_state *data = term->data; + data->port->driver->put (data->port, c); +} + +static int +serial_fetch (grub_term_input_t term) +{ + struct grub_serial_input_state *data = term->data; + return data->port->driver->fetch (data->port); +} + +static const struct grub_serial_input_state grub_serial_terminfo_input_template = + { + .tinfo = + { + .readkey = serial_fetch + } + }; + +static const struct grub_serial_output_state grub_serial_terminfo_output_template = + { + .tinfo = + { + .put = serial_put, + .size = { 80, 24 } + } + }; + +static struct grub_serial_input_state grub_serial_terminfo_input; + +static struct grub_serial_output_state grub_serial_terminfo_output; + +static int registered = 0; + +static struct grub_term_input grub_serial_term_input = +{ + .name = "serial", + .init = grub_terminfo_input_init, + .getkey = grub_terminfo_getkey, + .data = &grub_serial_terminfo_input +}; + +static struct grub_term_output grub_serial_term_output = +{ + .name = "serial", + .init = grub_terminfo_output_init, + .putchar = grub_terminfo_putchar, + .getwh = grub_terminfo_getwh, + .getxy = grub_terminfo_getxy, + .gotoxy = grub_terminfo_gotoxy, + .cls = grub_terminfo_cls, + .setcolorstate = grub_terminfo_setcolorstate, + .setcursor = grub_terminfo_setcursor, + .flags = GRUB_TERM_CODE_TYPE_ASCII, + .data = &grub_serial_terminfo_output, + .progress_update_divisor = GRUB_PROGRESS_SLOW +}; + + + +struct grub_serial_port * +grub_serial_find (const char *name) +{ + struct grub_serial_port *port; + + FOR_SERIAL_PORTS (port) + if (grub_strcmp (port->name, name) == 0) + break; + +#if (defined(__mips__) || defined (__i386__) || defined (__x86_64__)) && !defined(GRUB_MACHINE_EMU) && !defined(GRUB_MACHINE_ARC) + if (!port && grub_memcmp (name, "port", sizeof ("port") - 1) == 0 + && grub_isxdigit (name [sizeof ("port") - 1])) + { + name = grub_serial_ns8250_add_port (grub_strtoul (&name[sizeof ("port") - 1], + 0, 16)); + if (!name) + return NULL; + + FOR_SERIAL_PORTS (port) + if (grub_strcmp (port->name, name) == 0) + break; + } +#endif + +#ifdef GRUB_MACHINE_IEEE1275 + if (!port && grub_memcmp (name, "ieee1275/", sizeof ("ieee1275/") - 1) == 0) + { + name = grub_ofserial_add_port (&name[sizeof ("ieee1275/") - 1]); + if (!name) + return NULL; + + FOR_SERIAL_PORTS (port) + if (grub_strcmp (port->name, name) == 0) + break; + } +#endif + + return port; +} + +static grub_err_t +grub_cmd_serial (grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; + char pname[40]; + const char *name = NULL; + struct grub_serial_port *port; + struct grub_serial_config config; + grub_err_t err; + + if (state[OPTION_UNIT].set) + { + grub_snprintf (pname, sizeof (pname), "com%ld", + grub_strtoul (state[0].arg, 0, 0)); + name = pname; + } + + if (state[OPTION_PORT].set) + { + grub_snprintf (pname, sizeof (pname), "port%lx", + grub_strtoul (state[1].arg, 0, 0)); + name = pname; + } + + if (argc >= 1) + name = args[0]; + + if (!name) + name = "com0"; + + port = grub_serial_find (name); + if (!port) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("serial port `%s' isn't found"), + name); + + config = port->config; + + if (state[OPTION_SPEED].set) { + config.speed = grub_strtoul (state[OPTION_SPEED].arg, 0, 0); + if (config.speed == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port parity")); + } + + if (state[OPTION_WORD].set) + config.word_len = grub_strtoul (state[OPTION_WORD].arg, 0, 0); + + if (state[OPTION_PARITY].set) + { + if (! grub_strcmp (state[OPTION_PARITY].arg, "no")) + config.parity = GRUB_SERIAL_PARITY_NONE; + else if (! grub_strcmp (state[OPTION_PARITY].arg, "odd")) + config.parity = GRUB_SERIAL_PARITY_ODD; + else if (! grub_strcmp (state[OPTION_PARITY].arg, "even")) + config.parity = GRUB_SERIAL_PARITY_EVEN; + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port parity")); + } + + if (state[OPTION_RTSCTS].set) + { + if (grub_strcmp (state[OPTION_RTSCTS].arg, "on") == 0) + config.rtscts = 1; + else if (grub_strcmp (state[OPTION_RTSCTS].arg, "off") == 0) + config.rtscts = 0; + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port flow control")); + } + + if (state[OPTION_STOP].set) + { + if (! grub_strcmp (state[OPTION_STOP].arg, "1")) + config.stop_bits = GRUB_SERIAL_STOP_BITS_1; + else if (! grub_strcmp (state[OPTION_STOP].arg, "2")) + config.stop_bits = GRUB_SERIAL_STOP_BITS_2; + else if (! grub_strcmp (state[OPTION_STOP].arg, "1.5")) + config.stop_bits = GRUB_SERIAL_STOP_BITS_1_5; + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port stop bits number")); + } + + if (state[OPTION_BASE_CLOCK].set) + { + const char *ptr; + config.base_clock = grub_strtoull (state[OPTION_BASE_CLOCK].arg, &ptr, 0); + if (grub_errno) + return grub_errno; + if (ptr && *ptr == 'M') + config.base_clock *= 1000000; + if (ptr && (*ptr == 'k' || *ptr == 'K')) + config.base_clock *= 1000; + } + + if (config.speed == 0) + config.speed = 9600; + + /* Initialize with new settings. */ + err = port->driver->configure (port, &config); + if (err) + return err; +#if !defined (GRUB_MACHINE_EMU) && !defined(GRUB_MACHINE_ARC) && (defined(__mips__) || defined (__i386__) || defined (__x86_64__)) + + /* Compatibility kludge. */ + if (port->driver == &grub_ns8250_driver) + { + if (!registered) + { + grub_terminfo_output_register (&grub_serial_term_output, "vt100"); + + grub_term_register_input ("serial", &grub_serial_term_input); + grub_term_register_output ("serial", &grub_serial_term_output); + } + grub_serial_terminfo_output.port = port; + grub_serial_terminfo_input.port = port; + registered = 1; + } +#endif + return GRUB_ERR_NONE; +} + +#ifdef GRUB_MACHINE_MIPS_LOONGSON +const char loongson_defserial[][6] = + { + [GRUB_ARCH_MACHINE_YEELOONG] = "com0", + [GRUB_ARCH_MACHINE_FULOONG2F] = "com2", + [GRUB_ARCH_MACHINE_FULOONG2E] = "com1" + }; +#endif + +grub_err_t +grub_serial_register (struct grub_serial_port *port) +{ + struct grub_term_input *in; + struct grub_term_output *out; + struct grub_serial_input_state *indata; + struct grub_serial_output_state *outdata; + + in = grub_malloc (sizeof (*in)); + if (!in) + return grub_errno; + + indata = grub_malloc (sizeof (*indata)); + if (!indata) + { + grub_free (in); + return grub_errno; + } + + grub_memcpy (in, &grub_serial_term_input, sizeof (*in)); + in->data = indata; + in->name = grub_xasprintf ("serial_%s", port->name); + grub_memcpy (indata, &grub_serial_terminfo_input, sizeof (*indata)); + + if (!in->name) + { + grub_free (in); + grub_free (indata); + return grub_errno; + } + + out = grub_zalloc (sizeof (*out)); + if (!out) + { + grub_free (indata); + grub_free ((char *) in->name); + grub_free (in); + return grub_errno; + } + + outdata = grub_malloc (sizeof (*outdata)); + if (!outdata) + { + grub_free (indata); + grub_free ((char *) in->name); + grub_free (out); + grub_free (in); + return grub_errno; + } + + grub_memcpy (out, &grub_serial_term_output, sizeof (*out)); + out->data = outdata; + out->name = in->name; + grub_memcpy (outdata, &grub_serial_terminfo_output, sizeof (*outdata)); + + grub_list_push (GRUB_AS_LIST_P (&grub_serial_ports), GRUB_AS_LIST (port)); + ((struct grub_serial_input_state *) in->data)->port = port; + ((struct grub_serial_output_state *) out->data)->port = port; + port->term_in = in; + port->term_out = out; + grub_terminfo_output_register (out, "vt100"); +#ifdef GRUB_MACHINE_MIPS_LOONGSON + if (grub_strcmp (port->name, loongson_defserial[grub_arch_machine]) == 0) + { + grub_term_register_input_active ("serial_*", in); + grub_term_register_output_active ("serial_*", out); + } + else + { + grub_term_register_input_inactive ("serial_*", in); + grub_term_register_output_inactive ("serial_*", out); + } +#else + grub_term_register_input ("serial_*", in); + grub_term_register_output ("serial_*", out); +#endif + + return GRUB_ERR_NONE; +} + +void +grub_serial_unregister (struct grub_serial_port *port) +{ + if (port->driver->fini) + port->driver->fini (port); + + if (port->term_in) + grub_term_unregister_input (port->term_in); + if (port->term_out) + grub_term_unregister_output (port->term_out); + + grub_list_remove (GRUB_AS_LIST (port)); +} + +void +grub_serial_unregister_driver (struct grub_serial_driver *driver) +{ + struct grub_serial_port *port, *next; + for (port = grub_serial_ports; port; port = next) + { + next = port->next; + if (port->driver == driver) + grub_serial_unregister (port); + } +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(serial) +{ + cmd = grub_register_extcmd ("serial", grub_cmd_serial, 0, + N_("[OPTIONS...]"), + N_("Configure serial port."), options); + grub_memcpy (&grub_serial_terminfo_output, + &grub_serial_terminfo_output_template, + sizeof (grub_serial_terminfo_output)); + + grub_memcpy (&grub_serial_terminfo_input, + &grub_serial_terminfo_input_template, + sizeof (grub_serial_terminfo_input)); + +#if !defined (GRUB_MACHINE_EMU) && !defined(GRUB_MACHINE_ARC) && (defined(__mips__) || defined (__i386__) || defined (__x86_64__)) + grub_ns8250_init (); +#endif +#ifdef GRUB_MACHINE_IEEE1275 + grub_ofserial_init (); +#endif +#ifdef GRUB_MACHINE_EFI + grub_efiserial_init (); +#endif +#ifdef GRUB_MACHINE_ARC + grub_arcserial_init (); +#endif +} + +GRUB_MOD_FINI(serial) +{ + while (grub_serial_ports) + grub_serial_unregister (grub_serial_ports); + if (registered) + { + grub_term_unregister_input (&grub_serial_term_input); + grub_term_unregister_output (&grub_serial_term_output); + } + grub_unregister_extcmd (cmd); +} diff --git a/grub-core/term/spkmodem.c b/grub-core/term/spkmodem.c new file mode 100644 index 0000000..75c8a0f --- /dev/null +++ b/grub-core/term/spkmodem.c @@ -0,0 +1,141 @@ +/* console.c -- Open Firmware console for GRUB. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +extern struct grub_terminfo_output_state grub_spkmodem_terminfo_output; + +static void +make_tone (grub_uint16_t freq_count, unsigned int duration) +{ + /* Program timer 2. */ + grub_outb (GRUB_PIT_CTRL_SELECT_2 + | GRUB_PIT_CTRL_READLOAD_WORD + | GRUB_PIT_CTRL_SQUAREWAVE_GEN + | GRUB_PIT_CTRL_COUNT_BINARY, GRUB_PIT_CTRL); + grub_outb (freq_count & 0xff, GRUB_PIT_COUNTER_2); /* LSB */ + grub_outb ((freq_count >> 8) & 0xff, GRUB_PIT_COUNTER_2); /* MSB */ + + /* Start speaker. */ + grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT) + | GRUB_PIT_SPK_TMR2 | GRUB_PIT_SPK_DATA, + GRUB_PIT_SPEAKER_PORT); + + for (; duration; duration--) + { + unsigned short counter, previous_counter = 0xffff; + while (1) + { + counter = grub_inb (GRUB_PIT_COUNTER_2); + counter |= ((grub_uint16_t) grub_inb (GRUB_PIT_COUNTER_2)) << 8; + if (counter > previous_counter) + { + previous_counter = counter; + break; + } + previous_counter = counter; + } + } +} + +static int inited; + +static void +put (struct grub_term_output *term __attribute__ ((unused)), const int c) +{ + int i; + + make_tone (GRUB_SPEAKER_PIT_FREQUENCY / 200, 4); + for (i = 7; i >= 0; i--) + { + if ((c >> i) & 1) + make_tone (GRUB_SPEAKER_PIT_FREQUENCY / 2000, 20); + else + make_tone (GRUB_SPEAKER_PIT_FREQUENCY / 4000, 40); + make_tone (GRUB_SPEAKER_PIT_FREQUENCY / 1000, 10); + } + make_tone (GRUB_SPEAKER_PIT_FREQUENCY / 200, 0); +} + +static grub_err_t +grub_spkmodem_init_output (struct grub_term_output *term) +{ + /* Some models shutdown sound when not in use and it takes for it + around 30 ms to come back on which loses 3 bits. So generate a base + 200 Hz continously. */ + + make_tone (GRUB_SPEAKER_PIT_FREQUENCY / 200, 0); + grub_terminfo_output_init (term); + + return 0; +} + +static grub_err_t +grub_spkmodem_fini_output (struct grub_term_output *term __attribute__ ((unused))) +{ + grub_speaker_beep_off (); + inited = 0; + return 0; +} + + + + +struct grub_terminfo_output_state grub_spkmodem_terminfo_output = + { + .put = put, + .size = { 80, 24 } + }; + +static struct grub_term_output grub_spkmodem_term_output = + { + .name = "spkmodem", + .init = grub_spkmodem_init_output, + .fini = grub_spkmodem_fini_output, + .putchar = grub_terminfo_putchar, + .getxy = grub_terminfo_getxy, + .getwh = grub_terminfo_getwh, + .gotoxy = grub_terminfo_gotoxy, + .cls = grub_terminfo_cls, + .setcolorstate = grub_terminfo_setcolorstate, + .setcursor = grub_terminfo_setcursor, + .flags = GRUB_TERM_CODE_TYPE_ASCII, + .data = &grub_spkmodem_terminfo_output, + .progress_update_divisor = GRUB_PROGRESS_NO_UPDATE + }; + +GRUB_MOD_INIT (spkmodem) +{ + grub_term_register_output ("spkmodem", &grub_spkmodem_term_output); +} + + +GRUB_MOD_FINI (spkmodem) +{ + grub_term_unregister_output (&grub_spkmodem_term_output); +} diff --git a/grub-core/term/terminfo.c b/grub-core/term/terminfo.c new file mode 100644 index 0000000..85ecf06 --- /dev/null +++ b/grub-core/term/terminfo.c @@ -0,0 +1,796 @@ +/* terminfo.c - simple terminfo module */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2004,2005,2007 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +/* + * This file contains various functions dealing with different + * terminal capabilities. For example, vt52 and vt100. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275) +#include +#endif + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define ANSI_CSI 0x9b +#define ANSI_CSI_STR "\x9b" + +static struct grub_term_output *terminfo_outputs; + +/* Get current terminfo name. */ +char * +grub_terminfo_get_current (struct grub_term_output *term) +{ + struct grub_terminfo_output_state *data + = (struct grub_terminfo_output_state *) term->data; + return data->name; +} + +/* Free *PTR and set *PTR to NULL, to prevent double-free. */ +static void +grub_terminfo_free (char **ptr) +{ + grub_free (*ptr); + *ptr = 0; +} + +static void +grub_terminfo_all_free (struct grub_term_output *term) +{ + struct grub_terminfo_output_state *data + = (struct grub_terminfo_output_state *) term->data; + + /* Free previously allocated memory. */ + grub_terminfo_free (&data->name); + grub_terminfo_free (&data->gotoxy); + grub_terminfo_free (&data->cls); + grub_terminfo_free (&data->reverse_video_on); + grub_terminfo_free (&data->reverse_video_off); + grub_terminfo_free (&data->cursor_on); + grub_terminfo_free (&data->cursor_off); +} + +/* Set current terminfo type. */ +grub_err_t +grub_terminfo_set_current (struct grub_term_output *term, + const char *str) +{ + struct grub_terminfo_output_state *data + = (struct grub_terminfo_output_state *) term->data; + /* TODO + * Lookup user specified terminfo type. If found, set term variables + * as appropriate. Otherwise return an error. + * + * How should this be done? + * a. A static table included in this module. + * - I do not like this idea. + * b. A table stored in the configuration directory. + * - Users must convert their terminfo settings if we have not already. + * c. Look for terminfo files in the configuration directory. + * - /usr/share/terminfo is 6.3M on my system. + * - /usr/share/terminfo is not on most users boot partition. + * + Copying the terminfo files you want to use to the grub + * configuration directory is easier then (b). + * d. Your idea here. + */ + + grub_terminfo_all_free (term); + + if (grub_strcmp ("vt100", str) == 0) + { + data->name = grub_strdup ("vt100"); + data->gotoxy = grub_strdup ("\e[%i%p1%d;%p2%dH"); + data->cls = grub_strdup ("\e[H\e[J"); + data->reverse_video_on = grub_strdup ("\e[7m"); + data->reverse_video_off = grub_strdup ("\e[m"); + data->cursor_on = grub_strdup ("\e[?25h"); + data->cursor_off = grub_strdup ("\e[?25l"); + data->setcolor = NULL; + return grub_errno; + } + + if (grub_strcmp ("vt100-color", str) == 0) + { + data->name = grub_strdup ("vt100-color"); + data->gotoxy = grub_strdup ("\e[%i%p1%d;%p2%dH"); + data->cls = grub_strdup ("\e[H\e[J"); + data->reverse_video_on = grub_strdup ("\e[7m"); + data->reverse_video_off = grub_strdup ("\e[m"); + data->cursor_on = grub_strdup ("\e[?25h"); + data->cursor_off = grub_strdup ("\e[?25l"); + data->setcolor = grub_strdup ("\e[3%p1%dm\e[4%p2%dm"); + return grub_errno; + } + + if (grub_strcmp ("arc", str) == 0) + { + data->name = grub_strdup ("arc"); + data->gotoxy = grub_strdup (ANSI_CSI_STR "%i%p1%d;%p2%dH"); + data->cls = grub_strdup (ANSI_CSI_STR "2J"); + data->reverse_video_on = grub_strdup (ANSI_CSI_STR "7m"); + data->reverse_video_off = grub_strdup (ANSI_CSI_STR "0m"); + data->cursor_on = 0; + data->cursor_off = 0; + data->setcolor = grub_strdup (ANSI_CSI_STR "3%p1%dm" + ANSI_CSI_STR "4%p2%dm"); + return grub_errno; + } + + if (grub_strcmp ("ieee1275", str) == 0 + || grub_strcmp ("ieee1275-nocursor", str) == 0) + { + data->name = grub_strdup ("ieee1275"); + data->gotoxy = grub_strdup ("\e[%i%p1%d;%p2%dH"); + /* Clear the screen. Using serial console, screen(1) only recognizes the + * ANSI escape sequence. Using video console, Apple Open Firmware + * (version 3.1.1) only recognizes the literal ^L. So use both. */ + data->cls = grub_strdup (" \e[2J"); + data->reverse_video_on = grub_strdup ("\e[7m"); + data->reverse_video_off = grub_strdup ("\e[m"); + if (grub_strcmp ("ieee1275", str) == 0) + { + data->cursor_on = grub_strdup ("\e[?25h"); + data->cursor_off = grub_strdup ("\e[?25l"); + } + else + { + data->cursor_on = 0; + data->cursor_off = 0; + } + data->setcolor = grub_strdup ("\e[3%p1%dm\e[4%p2%dm"); + return grub_errno; + } + + if (grub_strcmp ("dumb", str) == 0) + { + data->name = grub_strdup ("dumb"); + data->gotoxy = NULL; + data->cls = NULL; + data->reverse_video_on = NULL; + data->reverse_video_off = NULL; + data->cursor_on = NULL; + data->cursor_off = NULL; + data->setcolor = NULL; + return grub_errno; + } + + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unknown terminfo type `%s'"), + str); +} + +grub_err_t +grub_terminfo_output_register (struct grub_term_output *term, + const char *type) +{ + grub_err_t err; + struct grub_terminfo_output_state *data; + + err = grub_terminfo_set_current (term, type); + + if (err) + return err; + + data = (struct grub_terminfo_output_state *) term->data; + data->next = terminfo_outputs; + terminfo_outputs = term; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_terminfo_output_unregister (struct grub_term_output *term) +{ + struct grub_term_output **ptr; + + for (ptr = &terminfo_outputs; *ptr; + ptr = &((struct grub_terminfo_output_state *) (*ptr)->data)->next) + if (*ptr == term) + { + grub_terminfo_all_free (term); + *ptr = ((struct grub_terminfo_output_state *) (*ptr)->data)->next; + return GRUB_ERR_NONE; + } + return grub_error (GRUB_ERR_BUG, "terminal not found"); +} + +/* Wrapper for grub_putchar to write strings. */ +static void +putstr (struct grub_term_output *term, const char *str) +{ + struct grub_terminfo_output_state *data + = (struct grub_terminfo_output_state *) term->data; + while (*str) + data->put (term, *str++); +} + +struct grub_term_coordinate +grub_terminfo_getxy (struct grub_term_output *term) +{ + struct grub_terminfo_output_state *data + = (struct grub_terminfo_output_state *) term->data; + + return data->pos; +} + +void +grub_terminfo_gotoxy (struct grub_term_output *term, + struct grub_term_coordinate pos) +{ + struct grub_terminfo_output_state *data + = (struct grub_terminfo_output_state *) term->data; + + if (pos.x > grub_term_width (term) || pos.y > grub_term_height (term)) + { + grub_error (GRUB_ERR_BUG, "invalid point (%u,%u)", pos.x, pos.y); + return; + } + + if (data->gotoxy) + putstr (term, grub_terminfo_tparm (data->gotoxy, pos.y, pos.x)); + else + { + if ((pos.y == data->pos.y) && (pos.x == data->pos.x - 1)) + data->put (term, '\b'); + } + + data->pos = pos; +} + +/* Clear the screen. */ +void +grub_terminfo_cls (struct grub_term_output *term) +{ + struct grub_terminfo_output_state *data + = (struct grub_terminfo_output_state *) term->data; + + putstr (term, grub_terminfo_tparm (data->cls)); + grub_terminfo_gotoxy (term, (struct grub_term_coordinate) { 0, 0 }); +} + +void +grub_terminfo_setcolorstate (struct grub_term_output *term, + const grub_term_color_state state) +{ + struct grub_terminfo_output_state *data + = (struct grub_terminfo_output_state *) term->data; + + if (data->setcolor) + { + int fg; + int bg; + /* Map from VGA to terminal colors. */ + const int colormap[8] + = { 0, /* Black. */ + 4, /* Blue. */ + 2, /* Green. */ + 6, /* Cyan. */ + 1, /* Red. */ + 5, /* Magenta. */ + 3, /* Yellow. */ + 7, /* White. */ + }; + + switch (state) + { + case GRUB_TERM_COLOR_STANDARD: + case GRUB_TERM_COLOR_NORMAL: + fg = grub_term_normal_color & 0x0f; + bg = grub_term_normal_color >> 4; + break; + case GRUB_TERM_COLOR_HIGHLIGHT: + fg = grub_term_highlight_color & 0x0f; + bg = grub_term_highlight_color >> 4; + break; + default: + return; + } + + putstr (term, grub_terminfo_tparm (data->setcolor, colormap[fg & 7], + colormap[bg & 7])); + return; + } + + switch (state) + { + case GRUB_TERM_COLOR_STANDARD: + case GRUB_TERM_COLOR_NORMAL: + putstr (term, grub_terminfo_tparm (data->reverse_video_off)); + break; + case GRUB_TERM_COLOR_HIGHLIGHT: + putstr (term, grub_terminfo_tparm (data->reverse_video_on)); + break; + default: + break; + } +} + +void +grub_terminfo_setcursor (struct grub_term_output *term, const int on) +{ + struct grub_terminfo_output_state *data + = (struct grub_terminfo_output_state *) term->data; + + if (on) + putstr (term, grub_terminfo_tparm (data->cursor_on)); + else + putstr (term, grub_terminfo_tparm (data->cursor_off)); +} + +/* The terminfo version of putchar. */ +void +grub_terminfo_putchar (struct grub_term_output *term, + const struct grub_unicode_glyph *c) +{ + struct grub_terminfo_output_state *data + = (struct grub_terminfo_output_state *) term->data; + + /* Keep track of the cursor. */ + switch (c->base) + { + case '\a': + break; + + case '\b': + case 127: + if (data->pos.x > 0) + data->pos.x--; + break; + + case '\n': + if (data->pos.y < grub_term_height (term) - 1) + data->pos.y++; + break; + + case '\r': + data->pos.x = 0; + break; + + default: + if ((int) data->pos.x + c->estimated_width >= (int) grub_term_width (term) + 1) + { + data->pos.x = 0; + if (data->pos.y < grub_term_height (term) - 1) + data->pos.y++; + data->put (term, '\r'); + data->put (term, '\n'); + } + data->pos.x += c->estimated_width; + break; + } + + data->put (term, c->base); +} + +struct grub_term_coordinate +grub_terminfo_getwh (struct grub_term_output *term) +{ + struct grub_terminfo_output_state *data + = (struct grub_terminfo_output_state *) term->data; + + return data->size; +} + +static void +grub_terminfo_readkey (struct grub_term_input *term, int *keys, int *len, int max_len, + int (*readkey) (struct grub_term_input *term)) +{ + int c; + +#define CONTINUE_READ \ + { \ + grub_uint64_t start; \ + /* On 9600 we have to wait up to 12 milliseconds. */ \ + start = grub_get_time_ms (); \ + do \ + c = readkey (term); \ + while (c == -1 && grub_get_time_ms () - start < 100); \ + if (c == -1) \ + return; \ + \ + if (*len >= max_len) \ + return; \ + \ + keys[*len] = c; \ + (*len)++; \ + } + + c = readkey (term); + if (c < 0) + { + *len = 0; + return; + } + *len = 1; + keys[0] = c; + if (c != ANSI_CSI && c != GRUB_TERM_ESC) + { + /* Backspace: Ctrl-h. */ + if (c == 0x7f) + c = GRUB_TERM_BACKSPACE; + if (c < 0x20 && c != GRUB_TERM_TAB && c!= GRUB_TERM_BACKSPACE && c != '\n' && c != '\r') + c = GRUB_TERM_CTRL | (c - 1 + 'a'); + *len = 1; + keys[0] = c; + return; + } + + { + static struct + { + char key; + unsigned ascii; + } + three_code_table[] = + { + {'4', GRUB_TERM_KEY_DC}, + {'A', GRUB_TERM_KEY_UP}, + {'B', GRUB_TERM_KEY_DOWN}, + {'C', GRUB_TERM_KEY_RIGHT}, + {'D', GRUB_TERM_KEY_LEFT}, + {'F', GRUB_TERM_KEY_END}, + {'H', GRUB_TERM_KEY_HOME}, + {'K', GRUB_TERM_KEY_END}, + {'P', GRUB_TERM_KEY_DC}, + {'?', GRUB_TERM_KEY_PPAGE}, + {'/', GRUB_TERM_KEY_NPAGE}, + {'@', GRUB_TERM_KEY_INSERT}, + }; + + static unsigned four_code_table[] = + { + [1] = GRUB_TERM_KEY_HOME, + [3] = GRUB_TERM_KEY_DC, + [5] = GRUB_TERM_KEY_PPAGE, + [6] = GRUB_TERM_KEY_NPAGE, + [7] = GRUB_TERM_KEY_HOME, + [8] = GRUB_TERM_KEY_END, + [17] = GRUB_TERM_KEY_F6, + [18] = GRUB_TERM_KEY_F7, + [19] = GRUB_TERM_KEY_F8, + [20] = GRUB_TERM_KEY_F9, + [21] = GRUB_TERM_KEY_F10, + [23] = GRUB_TERM_KEY_F11, + [24] = GRUB_TERM_KEY_F12, + }; + char fx_key[] = + { 'P', 'Q', 'w', 'x', 't', 'u', + 'q', 'r', 'p', 'M', 'A', 'B', 'H', 'F' }; + unsigned fx_code[] = + { GRUB_TERM_KEY_F1, GRUB_TERM_KEY_F2, GRUB_TERM_KEY_F3, + GRUB_TERM_KEY_F4, GRUB_TERM_KEY_F5, GRUB_TERM_KEY_F6, + GRUB_TERM_KEY_F7, GRUB_TERM_KEY_F8, GRUB_TERM_KEY_F9, + GRUB_TERM_KEY_F10, GRUB_TERM_KEY_F11, GRUB_TERM_KEY_F12, + GRUB_TERM_KEY_HOME, GRUB_TERM_KEY_END }; + unsigned i; + + if (c == GRUB_TERM_ESC) + { + CONTINUE_READ; + + if (c == 'O') + { + CONTINUE_READ; + + for (i = 0; i < ARRAY_SIZE (fx_key); i++) + if (fx_key[i] == c) + { + keys[0] = fx_code[i]; + *len = 1; + return; + } + } + + if (c != '[') + return; + } + + CONTINUE_READ; + + for (i = 0; i < ARRAY_SIZE (three_code_table); i++) + if (three_code_table[i].key == c) + { + keys[0] = three_code_table[i].ascii; + *len = 1; + return; + } + + switch (c) + { + case '[': + CONTINUE_READ; + if (c >= 'A' && c <= 'E') + { + keys[0] = GRUB_TERM_KEY_F1 + c - 'A'; + *len = 1; + return; + } + return; + case 'O': + CONTINUE_READ; + for (i = 0; i < ARRAY_SIZE (fx_key); i++) + if (fx_key[i] == c) + { + keys[0] = fx_code[i]; + *len = 1; + return; + } + return; + + case '0': + { + int num = 0; + CONTINUE_READ; + if (c != '0' && c != '1') + return; + num = (c - '0') * 10; + CONTINUE_READ; + if (c < '0' || c > '9') + return; + num += (c - '0'); + if (num == 0 || num > 12) + return; + CONTINUE_READ; + if (c != 'q') + return; + keys[0] = fx_code[num - 1]; + *len = 1; + return; + } + + case '1' ... '9': + { + unsigned val = c - '0'; + CONTINUE_READ; + if (c >= '0' && c <= '9') + { + val = val * 10 + (c - '0'); + CONTINUE_READ; + } + if (c != '~') + return; + if (val >= ARRAY_SIZE (four_code_table) + || four_code_table[val] == 0) + return; + keys[0] = four_code_table[val]; + *len = 1; + return; + } + default: + return; + } + } +#undef CONTINUE_READ +} + +/* The terminfo version of getkey. */ +int +grub_terminfo_getkey (struct grub_term_input *termi) +{ + struct grub_terminfo_input_state *data + = (struct grub_terminfo_input_state *) (termi->data); + if (data->npending) + { + int ret; + data->npending--; + ret = data->input_buf[0]; + grub_memmove (data->input_buf, data->input_buf + 1, data->npending + * sizeof (data->input_buf[0])); + return ret; + } + + grub_terminfo_readkey (termi, data->input_buf, &data->npending, + GRUB_TERMINFO_READKEY_MAX_LEN, data->readkey); + +#if defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275) + if (data->npending == 1 && data->input_buf[0] == GRUB_TERM_ESC + && grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_REPEAT) + && grub_get_time_ms () - data->last_key_time < 1000 + && (data->last_key & GRUB_TERM_EXTENDED)) + { + data->npending = 0; + data->last_key_time = grub_get_time_ms (); + return data->last_key; + } +#endif + + if (data->npending) + { + int ret; + data->npending--; + ret = data->input_buf[0]; +#if defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275) + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_REPEAT)) + { + data->last_key = ret; + data->last_key_time = grub_get_time_ms (); + } +#endif + grub_memmove (data->input_buf, data->input_buf + 1, data->npending + * sizeof (data->input_buf[0])); + return ret; + } + + return GRUB_TERM_NO_KEY; +} + +grub_err_t +grub_terminfo_input_init (struct grub_term_input *termi) +{ + struct grub_terminfo_input_state *data + = (struct grub_terminfo_input_state *) (termi->data); + data->npending = 0; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_terminfo_output_init (struct grub_term_output *term) +{ + grub_terminfo_cls (term); + return GRUB_ERR_NONE; +} + +/* GRUB Command. */ + +static grub_err_t +print_terminfo (void) +{ + const char *encoding_names[(GRUB_TERM_CODE_TYPE_MASK + >> GRUB_TERM_CODE_TYPE_SHIFT) + 1] + = { + /* VGA and glyph descriptor types are just for completeness, + they are not used on terminfo terminals. + */ + [GRUB_TERM_CODE_TYPE_ASCII >> GRUB_TERM_CODE_TYPE_SHIFT] = _("ASCII"), + [GRUB_TERM_CODE_TYPE_CP437 >> GRUB_TERM_CODE_TYPE_SHIFT] = "CP-437", + [GRUB_TERM_CODE_TYPE_UTF8_LOGICAL >> GRUB_TERM_CODE_TYPE_SHIFT] + = _("UTF-8"), + [GRUB_TERM_CODE_TYPE_UTF8_VISUAL >> GRUB_TERM_CODE_TYPE_SHIFT] + /* TRANSLATORS: visually ordered UTF-8 is a non-compliant encoding + based on UTF-8 with right-to-left languages written in reverse. + Used on some terminals. Normal UTF-8 is refered as + "logically-ordered UTF-8" by opposition. */ + = _("visually-ordered UTF-8"), + [GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS >> GRUB_TERM_CODE_TYPE_SHIFT] + = "Glyph descriptors", + _("Unknown encoding"), _("Unknown encoding"), _("Unknown encoding") + }; + struct grub_term_output *cur; + + grub_puts_ (N_("Current terminfo types:")); + for (cur = terminfo_outputs; cur; + cur = ((struct grub_terminfo_output_state *) cur->data)->next) + grub_printf ("%s: %s\t%s\t%dx%d\n", cur->name, + grub_terminfo_get_current(cur), + encoding_names[(cur->flags & GRUB_TERM_CODE_TYPE_MASK) + >> GRUB_TERM_CODE_TYPE_SHIFT], + ((struct grub_terminfo_output_state *) cur->data)->pos.x, + ((struct grub_terminfo_output_state *) cur->data)->pos.y); + + return GRUB_ERR_NONE; +} + +static const struct grub_arg_option options[] = +{ + {"ascii", 'a', 0, N_("Terminal is ASCII-only [default]."), 0, ARG_TYPE_NONE}, + {"utf8", 'u', 0, N_("Terminal is logical-ordered UTF-8."), 0, ARG_TYPE_NONE}, + {"visual-utf8", 'v', 0, N_("Terminal is visually-ordered UTF-8."), 0, + ARG_TYPE_NONE}, + {"geometry", 'g', 0, N_("Terminal has specified geometry."), + /* TRANSLATORS: "x" has to be entered in, like an identifier, so please don't + use better Unicode codepoints. */ + N_("WIDTHxHEIGHT."), ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} +}; + +enum + { + OPTION_ASCII, + OPTION_UTF8, + OPTION_VISUAL_UTF8, + OPTION_GEOMETRY + }; + +static grub_err_t +grub_cmd_terminfo (grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_term_output *cur; + int encoding = GRUB_TERM_CODE_TYPE_ASCII; + struct grub_arg_list *state = ctxt->state; + int w = 0, h = 0; + + if (argc == 0) + return print_terminfo (); + + if (state[OPTION_ASCII].set) + encoding = GRUB_TERM_CODE_TYPE_ASCII; + + if (state[OPTION_UTF8].set) + encoding = GRUB_TERM_CODE_TYPE_UTF8_LOGICAL; + + if (state[OPTION_VISUAL_UTF8].set) + encoding = GRUB_TERM_CODE_TYPE_UTF8_VISUAL; + + if (state[OPTION_GEOMETRY].set) + { + const char *ptr = state[OPTION_GEOMETRY].arg; + w = grub_strtoul (ptr, &ptr, 0); + if (grub_errno) + return grub_errno; + if (*ptr != 'x') + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("incorrect terminal dimensions specification")); + ptr++; + h = grub_strtoul (ptr, &ptr, 0); + if (grub_errno) + return grub_errno; + } + + for (cur = terminfo_outputs; cur; + cur = ((struct grub_terminfo_output_state *) cur->data)->next) + if (grub_strcmp (args[0], cur->name) == 0 + || (grub_strcmp (args[0], "ofconsole") == 0 + && grub_strcmp ("console", cur->name) == 0)) + { + cur->flags = (cur->flags & ~GRUB_TERM_CODE_TYPE_MASK) | encoding; + + if (w && h) + { + struct grub_terminfo_output_state *data + = (struct grub_terminfo_output_state *) cur->data; + data->size.x = w; + data->size.y = h; + } + + if (argc == 1) + return GRUB_ERR_NONE; + + return grub_terminfo_set_current (cur, args[1]); + } + + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("terminal %s isn't found or it's not handled by terminfo"), + args[0]); +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(terminfo) +{ + cmd = grub_register_extcmd ("terminfo", grub_cmd_terminfo, 0, + N_("[[-a|-u|-v] [-g WxH] [TERM] [TYPE]]"), + N_("Set terminfo type of TERM to TYPE.\n"), + options); +} + +GRUB_MOD_FINI(terminfo) +{ + grub_unregister_extcmd (cmd); +} diff --git a/grub-core/term/tparm.c b/grub-core/term/tparm.c new file mode 100644 index 0000000..fb5b15a --- /dev/null +++ b/grub-core/term/tparm.c @@ -0,0 +1,767 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1998-2003,2004,2005 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +/********************************************************************** + * This code is a modification of lib_tparm.c found in ncurses-5.2. The + * modification are for use in grub by replacing all libc function through + * special grub functions. This also meant to delete all dynamic memory + * allocation and replace it by a number of fixed buffers. + * + * Modifications by Tilmann Bubeck 2002 + * + * Resync with ncurses-5.4 by Omniflux 2005 + **********************************************************************/ + +/**************************************************************************** + * Author: Zeyd M. Ben-Halim 1992,1995 * + * and: Eric S. Raymond * + * and: Thomas E. Dickey, 1996 on * + ****************************************************************************/ + +/* + * tparm.c + * + */ + +#include +#include +#include +#include + +/* + * Common/troublesome character definitions + */ +typedef char grub_bool_t; +#ifndef FALSE +# define FALSE (0) +#endif +#ifndef TRUE +# define TRUE (!FALSE) +#endif + +#define NUM_PARM 9 +#define NUM_VARS 26 +#define STACKSIZE 20 +#define MAX_FORMAT_LEN 256 + +#define max(a,b) ((a) > (b) ? (a) : (b)) +#define isdigit(c) ((c) >= '0' && (c) <= '9') +#define isUPPER(c) ((c) >= 'A' && (c) <= 'Z') +#define isLOWER(c) ((c) >= 'a' && (c) <= 'z') + +#define UChar(c) ((unsigned char)(c)) + +//MODULE_ID("$Id$") + +/* + * char * + * tparm(string, ...) + * + * Substitute the given parameters into the given string by the following + * rules (taken from terminfo(5)): + * + * Cursor addressing and other strings requiring parame- + * ters in the terminal are described by a parameterized string + * capability, with like escapes %x in it. For example, to + * address the cursor, the cup capability is given, using two + * parameters: the row and column to address to. (Rows and + * columns are numbered from zero and refer to the physical + * screen visible to the user, not to any unseen memory.) If + * the terminal has memory relative cursor addressing, that can + * be indicated by + * + * The parameter mechanism uses a stack and special % + * codes to manipulate it. Typically a sequence will push one + * of the parameters onto the stack and then print it in some + * format. Often more complex operations are necessary. + * + * The % encodings have the following meanings: + * + * %% outputs `%' + * %c print pop() like %c in printf() + * %s print pop() like %s in printf() + * %[[:]flags][width[.precision]][doxXs] + * as in printf, flags are [-+#] and space + * The ':' is used to avoid making %+ or %- + * patterns (see below). + * + * %p[1-9] push ith parm + * %P[a-z] set dynamic variable [a-z] to pop() + * %g[a-z] get dynamic variable [a-z] and push it + * %P[A-Z] set static variable [A-Z] to pop() + * %g[A-Z] get static variable [A-Z] and push it + * %l push strlen(pop) + * %'c' push char constant c + * %{nn} push integer constant nn + * + * %+ %- %* %/ %m + * arithmetic (%m is mod): push(pop() op pop()) + * %& %| %^ bit operations: push(pop() op pop()) + * %= %> %< logical operations: push(pop() op pop()) + * %A %O logical and & or operations for conditionals + * %! %~ unary operations push(op pop()) + * %i add 1 to first two parms (for ANSI terminals) + * + * %? expr %t thenpart %e elsepart %; + * if-then-else, %e elsepart is optional. + * else-if's are possible ala Algol 68: + * %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %; + * + * For those of the above operators which are binary and not commutative, + * the stack works in the usual way, with + * %gx %gy %m + * resulting in x mod y, not the reverse. + */ + +typedef struct { + union { + int num; + char *str; + } data; + grub_bool_t num_type; +} stack_frame; + +static stack_frame stack[STACKSIZE]; +static int stack_ptr; +static const char *tparam_base = ""; + +static char *out_buff; +static grub_size_t out_size; +static grub_size_t out_used; + +static char *fmt_buff; +static grub_size_t fmt_size; + +static inline void +get_space(grub_size_t need) +{ + need += out_used; + if (need > out_size) { + out_size = need * 2; + out_buff = grub_realloc(out_buff, out_size*sizeof(char)); + /* FIX ME! handle out_buff == 0. */ + } +} + +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + +static inline void +save_text(const char *fmt, const char *s, int len) +{ + grub_size_t s_len = grub_strlen(s); + if (len > (int) s_len) + s_len = len; + + get_space(s_len + 1); + + (void) grub_snprintf(out_buff + out_used, s_len + 1, fmt, s); + out_used += grub_strlen(out_buff + out_used); +} + +static inline void +save_number(const char *fmt, int number, int len) +{ + if (len < 30) + len = 30; /* actually log10(MAX_INT)+1 */ + + get_space((unsigned) len + 1); + + (void) grub_snprintf(out_buff + out_used, len + 1, fmt, number); + out_used += grub_strlen(out_buff + out_used); +} + +#pragma GCC diagnostic error "-Wformat-nonliteral" + +static inline void +save_char(int c) +{ + if (c == 0) + c = 0200; + get_space(1); + out_buff[out_used++] = c; +} + +static inline void +npush(int x) +{ + if (stack_ptr < STACKSIZE) { + stack[stack_ptr].num_type = TRUE; + stack[stack_ptr].data.num = x; + stack_ptr++; + } +} + +static inline int +npop(void) +{ + int result = 0; + if (stack_ptr > 0) { + stack_ptr--; + if (stack[stack_ptr].num_type) + result = stack[stack_ptr].data.num; + } + return result; +} + +static inline void +spush(char *x) +{ + if (stack_ptr < STACKSIZE) { + stack[stack_ptr].num_type = FALSE; + stack[stack_ptr].data.str = x; + stack_ptr++; + } +} + +static inline char * +spop(void) +{ + static char dummy[] = ""; /* avoid const-cast */ + char *result = dummy; + if (stack_ptr > 0) { + stack_ptr--; + if (!stack[stack_ptr].num_type && stack[stack_ptr].data.str != 0) + result = stack[stack_ptr].data.str; + } + return result; +} + +static inline const char * +parse_format(const char *s, char *format, int *len) +{ + *len = 0; + if (format != 0) { + grub_bool_t done = FALSE; + grub_bool_t allowminus = FALSE; + grub_bool_t dot = FALSE; + grub_bool_t err = FALSE; + char *fmt = format; + int my_width = 0; + int my_prec = 0; + int value = 0; + + *len = 0; + *format++ = '%'; + while (*s != '\0' && !done) { + switch (*s) { + case 'c': /* FALLTHRU */ + case 'd': /* FALLTHRU */ + case 'o': /* FALLTHRU */ + case 'x': /* FALLTHRU */ + case 'X': /* FALLTHRU */ + case 's': + *format++ = *s; + done = TRUE; + break; + case '.': + *format++ = *s++; + if (dot) { + err = TRUE; + } else { /* value before '.' is the width */ + dot = TRUE; + my_width = value; + } + value = 0; + break; + case '#': + *format++ = *s++; + break; + case ' ': + *format++ = *s++; + break; + case ':': + s++; + allowminus = TRUE; + break; + case '-': + if (allowminus) { + *format++ = *s++; + } else { + done = TRUE; + } + break; + default: + if (isdigit(UChar(*s))) { + value = (value * 10) + (*s - '0'); + if (value > 10000) + err = TRUE; + *format++ = *s++; + } else { + done = TRUE; + } + } + } + + /* + * If we found an error, ignore (and remove) the flags. + */ + if (err) { + my_width = my_prec = value = 0; + format = fmt; + *format++ = '%'; + *format++ = *s; + } + + /* + * Any value after '.' is the precision. If we did not see '.', then + * the value is the width. + */ + if (dot) + my_prec = value; + else + my_width = value; + + *format = '\0'; + /* return maximum string length in print */ + *len = (my_width > my_prec) ? my_width : my_prec; + } + return s; +} + +/* + * Analyze the string to see how many parameters we need from the varargs list, + * and what their types are. We will only accept string parameters if they + * appear as a %l or %s format following an explicit parameter reference (e.g., + * %p2%s). All other parameters are numbers. + * + * 'number' counts coarsely the number of pop's we see in the string, and + * 'popcount' shows the highest parameter number in the string. We would like + * to simply use the latter count, but if we are reading termcap strings, there + * may be cases that we cannot see the explicit parameter numbers. + */ +static inline int +analyze(const char *string, char *p_is_s[NUM_PARM], int *popcount) +{ + grub_size_t len2; + int i; + int lastpop = -1; + int len; + int number = 0; + const char *cp = string; + static char dummy[] = ""; + + *popcount = 0; + + if (cp == 0) + return 0; + + if ((len2 = grub_strlen(cp)) > fmt_size) { + fmt_size = len2 + fmt_size + 2; + if ((fmt_buff = grub_realloc(fmt_buff, fmt_size*sizeof(char))) == 0) + return 0; + } + + grub_memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM); + + while ((cp - string) < (int) len2) { + if (*cp == '%') { + cp++; + cp = parse_format(cp, fmt_buff, &len); + switch (*cp) { + default: + break; + + case 'd': /* FALLTHRU */ + case 'o': /* FALLTHRU */ + case 'x': /* FALLTHRU */ + case 'X': /* FALLTHRU */ + case 'c': /* FALLTHRU */ + if (lastpop <= 0) + number++; + lastpop = -1; + break; + + case 'l': + case 's': + if (lastpop > 0) + p_is_s[lastpop - 1] = dummy; + ++number; + break; + + case 'p': + cp++; + i = (UChar(*cp) - '0'); + if (i >= 0 && i <= NUM_PARM) { + lastpop = i; + if (lastpop > *popcount) + *popcount = lastpop; + } + break; + + case 'P': + ++number; + ++cp; + break; + + case 'g': + cp++; + break; + + case '\'': + cp += 2; + lastpop = -1; + break; + + case '{': + cp++; + while (isdigit(UChar(*cp))) { + cp++; + } + break; + + case '+': + case '-': + case '*': + case '/': + case 'm': + case 'A': + case 'O': + case '&': + case '|': + case '^': + case '=': + case '<': + case '>': + lastpop = -1; + number += 2; + break; + + case '!': + case '~': + lastpop = -1; + ++number; + break; + + case 'i': + /* will add 1 to first (usually two) parameters */ + break; + } + } + if (*cp != '\0') + cp++; + } + + if (number > NUM_PARM) + number = NUM_PARM; + return number; +} + +static inline char * +tparam_internal(const char *string, va_list ap) +{ + char *p_is_s[NUM_PARM]; + long param[NUM_PARM]; + int popcount; + int number; + int len; + int level; + int x, y; + int i; + const char *cp = string; + grub_size_t len2; + static int dynamic_var[NUM_VARS]; + static int static_vars[NUM_VARS]; + + if (cp == 0) + return 0; + + out_used = out_size = fmt_size = 0; + + len2 = (int) grub_strlen(cp); + + /* + * Find the highest parameter-number referred to in the format string. + * Use this value to limit the number of arguments copied from the + * variable-length argument list. + */ + number = analyze(cp, p_is_s, &popcount); + if (fmt_buff == 0) + return 0; + + for (i = 0; i < max(popcount, number); i++) { + /* + * A few caps (such as plab_norm) have string-valued parms. + * We'll have to assume that the caller knows the difference, since + * a char* and an int may not be the same size on the stack. + */ + if (p_is_s[i] != 0) { + p_is_s[i] = va_arg(ap, char *); + } else { + param[i] = va_arg(ap, long int); + } + } + + /* + * This is a termcap compatibility hack. If there are no explicit pop + * operations in the string, load the stack in such a way that + * successive pops will grab successive parameters. That will make + * the expansion of (for example) \E[%d;%dH work correctly in termcap + * style, which means tparam() will expand termcap strings OK. + */ + stack_ptr = 0; + if (popcount == 0) { + popcount = number; + for (i = number - 1; i >= 0; i--) + npush(param[i]); + } + + while ((cp - string) < (int) len2) { + if (*cp != '%') { + save_char(UChar(*cp)); + } else { + tparam_base = cp++; + cp = parse_format(cp, fmt_buff, &len); + switch (*cp) { + default: + break; + case '%': + save_char('%'); + break; + + case 'd': /* FALLTHRU */ + case 'o': /* FALLTHRU */ + case 'x': /* FALLTHRU */ + case 'X': /* FALLTHRU */ + save_number(fmt_buff, npop(), len); + break; + + case 'c': /* FALLTHRU */ + save_char(npop()); + break; + + case 'l': + save_number("%d", (int) grub_strlen(spop()), 0); + break; + + case 's': + save_text(fmt_buff, spop(), len); + break; + + case 'p': + cp++; + i = (UChar(*cp) - '1'); + if (i >= 0 && i < NUM_PARM) { + if (p_is_s[i]) + spush(p_is_s[i]); + else + npush(param[i]); + } + break; + + case 'P': + cp++; + if (isUPPER(*cp)) { + i = (UChar(*cp) - 'A'); + static_vars[i] = npop(); + } else if (isLOWER(*cp)) { + i = (UChar(*cp) - 'a'); + dynamic_var[i] = npop(); + } + break; + + case 'g': + cp++; + if (isUPPER(*cp)) { + i = (UChar(*cp) - 'A'); + npush(static_vars[i]); + } else if (isLOWER(*cp)) { + i = (UChar(*cp) - 'a'); + npush(dynamic_var[i]); + } + break; + + case '\'': + cp++; + npush(UChar(*cp)); + cp++; + break; + + case '{': + number = 0; + cp++; + while (isdigit(UChar(*cp))) { + number = (number * 10) + (UChar(*cp) - '0'); + cp++; + } + npush(number); + break; + + case '+': + npush(npop() + npop()); + break; + + case '-': + y = npop(); + x = npop(); + npush(x - y); + break; + + case '*': + npush(npop() * npop()); + break; + + case '/': + y = npop(); + x = npop(); + /* GRUB has no signed divisions. */ + npush(y ? ((unsigned)x / (unsigned)y) : 0); + break; + + case 'm': + y = npop(); + x = npop(); + /* GRUB has no signed divisions. */ + npush(y ? ((unsigned)x % (unsigned)y) : 0); + break; + + case 'A': + npush(npop() && npop()); + break; + + case 'O': + npush(npop() || npop()); + break; + + case '&': + npush(npop() & npop()); + break; + + case '|': + npush(npop() | npop()); + break; + + case '^': + npush(npop() ^ npop()); + break; + + case '=': + y = npop(); + x = npop(); + npush(x == y); + break; + + case '<': + y = npop(); + x = npop(); + npush(x < y); + break; + + case '>': + y = npop(); + x = npop(); + npush(x > y); + break; + + case '!': + npush(!npop()); + break; + + case '~': + npush(~npop()); + break; + + case 'i': + if (p_is_s[0] == 0) + param[0]++; + if (p_is_s[1] == 0) + param[1]++; + break; + + case '?': + break; + + case 't': + x = npop(); + if (!x) { + /* scan forward for %e or %; at level zero */ + cp++; + level = 0; + while (*cp) { + if (*cp == '%') { + cp++; + if (*cp == '?') + level++; + else if (*cp == ';') { + if (level > 0) + level--; + else + break; + } else if (*cp == 'e' && level == 0) + break; + } + + if (*cp) + cp++; + } + } + break; + + case 'e': + /* scan forward for a %; at level zero */ + cp++; + level = 0; + while (*cp) { + if (*cp == '%') { + cp++; + if (*cp == '?') + level++; + else if (*cp == ';') { + if (level > 0) + level--; + else + break; + } + } + + if (*cp) + cp++; + } + break; + + case ';': + break; + + } /* endswitch (*cp) */ + } /* endelse (*cp == '%') */ + + if (*cp == '\0') + break; + + cp++; + } /* endwhile (*cp) */ + + get_space(1); + out_buff[out_used] = '\0'; + + return (out_buff); +} + +const char * +grub_terminfo_tparm (const char *string, ...) +{ + va_list ap; + char *result; + + if (!string) + return ""; + + va_start (ap, string); + result = tparam_internal (string, ap); + va_end (ap); + return result; +} diff --git a/grub-core/term/uboot/console.c b/grub-core/term/uboot/console.c new file mode 100644 index 0000000..dfdbe99 --- /dev/null +++ b/grub-core/term/uboot/console.c @@ -0,0 +1,132 @@ +/* console.c - console interface layer for U-Boot platforms */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +static void +put (struct grub_term_output *term __attribute__ ((unused)), const int c) +{ + grub_uboot_putc (c); +} + +static int +readkey (struct grub_term_input *term __attribute__ ((unused))) +{ + if (grub_uboot_tstc () > 0) + return grub_uboot_getc (); + + return -1; +} + +static void +uboot_console_setcursor (struct grub_term_output *term + __attribute__ ((unused)), int on + __attribute__ ((unused))) +{ + grub_terminfo_setcursor (term, on); +} + +static grub_err_t +uboot_console_init_input (struct grub_term_input *term) +{ + return grub_terminfo_input_init (term); +} + +extern struct grub_terminfo_output_state uboot_console_terminfo_output; + + +static grub_err_t +uboot_console_init_output (struct grub_term_output *term) +{ + grub_terminfo_output_init (term); + + return 0; +} + +struct grub_terminfo_input_state uboot_console_terminfo_input = { + .readkey = readkey +}; + +struct grub_terminfo_output_state uboot_console_terminfo_output = { + .put = put, + /* FIXME: In rare cases when console isn't serial, + determine real width. */ + .size = { 80, 24 } +}; + +static struct grub_term_input uboot_console_term_input = { + .name = "console", + .init = uboot_console_init_input, + .getkey = grub_terminfo_getkey, + .data = &uboot_console_terminfo_input +}; + +static struct grub_term_output uboot_console_term_output = { + .name = "console", + .init = uboot_console_init_output, + .putchar = grub_terminfo_putchar, + .getwh = grub_terminfo_getwh, + .getxy = grub_terminfo_getxy, + .gotoxy = grub_terminfo_gotoxy, + .cls = grub_terminfo_cls, + .setcolorstate = grub_terminfo_setcolorstate, + .setcursor = uboot_console_setcursor, + .flags = GRUB_TERM_CODE_TYPE_ASCII, + .data = &uboot_console_terminfo_output, + .progress_update_divisor = GRUB_PROGRESS_FAST +}; + +void +grub_console_init_early (void) +{ + grub_term_register_input ("console", &uboot_console_term_input); + grub_term_register_output ("console", &uboot_console_term_output); +} + + +/* + * grub_console_init_lately(): + * Initializes terminfo formatting by registering terminal type. + * Called after heap has been configured. + * + */ +void +grub_console_init_lately (void) +{ + const char *type; + + /* See if explicitly set by U-Boot environment */ + type = grub_uboot_env_get ("grub_term"); + if (!type) + type = "vt100"; + + grub_terminfo_init (); + grub_terminfo_output_register (&uboot_console_term_output, type); +} + +void +grub_console_fini (void) +{ +} diff --git a/grub-core/term/usb_keyboard.c b/grub-core/term/usb_keyboard.c new file mode 100644 index 0000000..e67b8f7 --- /dev/null +++ b/grub-core/term/usb_keyboard.c @@ -0,0 +1,471 @@ +/* Support for the HID Boot Protocol. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008, 2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + + + +enum + { + KEY_NO_KEY = 0x00, + KEY_ERR_BUFFER = 0x01, + KEY_ERR_POST = 0x02, + KEY_ERR_UNDEF = 0x03, + KEY_CAPS_LOCK = 0x39, + KEY_NUM_LOCK = 0x53, + }; + +enum + { + LED_NUM_LOCK = 0x01, + LED_CAPS_LOCK = 0x02 + }; + +/* Valid values for bRequest. See HID definition version 1.11 section 7.2. */ +#define USB_HID_GET_REPORT 0x01 +#define USB_HID_GET_IDLE 0x02 +#define USB_HID_GET_PROTOCOL 0x03 +#define USB_HID_SET_REPORT 0x09 +#define USB_HID_SET_IDLE 0x0A +#define USB_HID_SET_PROTOCOL 0x0B + +#define USB_HID_BOOT_SUBCLASS 0x01 +#define USB_HID_KBD_PROTOCOL 0x01 + +#define GRUB_USB_KEYBOARD_LEFT_CTRL 0x01 +#define GRUB_USB_KEYBOARD_LEFT_SHIFT 0x02 +#define GRUB_USB_KEYBOARD_LEFT_ALT 0x04 +#define GRUB_USB_KEYBOARD_RIGHT_CTRL 0x10 +#define GRUB_USB_KEYBOARD_RIGHT_SHIFT 0x20 +#define GRUB_USB_KEYBOARD_RIGHT_ALT 0x40 + +struct grub_usb_keyboard_data +{ + grub_usb_device_t usbdev; + grub_uint8_t status; + grub_uint16_t mods; + int interfno; + struct grub_usb_desc_endp *endp; + grub_usb_transfer_t transfer; + grub_uint8_t report[8]; + int dead; + int last_key; + grub_uint64_t repeat_time; + grub_uint8_t current_report[8]; + grub_uint8_t last_report[8]; + int index; + int max_index; +}; + +static int grub_usb_keyboard_getkey (struct grub_term_input *term); +static int grub_usb_keyboard_getkeystatus (struct grub_term_input *term); + +static struct grub_term_input grub_usb_keyboard_term = + { + .getkey = grub_usb_keyboard_getkey, + .getkeystatus = grub_usb_keyboard_getkeystatus, + .next = 0 + }; + +static struct grub_term_input grub_usb_keyboards[16]; + +static int +interpret_status (grub_uint8_t data0) +{ + int mods = 0; + + /* Check Shift, Control, and Alt status. */ + if (data0 & GRUB_USB_KEYBOARD_LEFT_SHIFT) + mods |= GRUB_TERM_STATUS_LSHIFT; + if (data0 & GRUB_USB_KEYBOARD_RIGHT_SHIFT) + mods |= GRUB_TERM_STATUS_RSHIFT; + if (data0 & GRUB_USB_KEYBOARD_LEFT_CTRL) + mods |= GRUB_TERM_STATUS_LCTRL; + if (data0 & GRUB_USB_KEYBOARD_RIGHT_CTRL) + mods |= GRUB_TERM_STATUS_RCTRL; + if (data0 & GRUB_USB_KEYBOARD_LEFT_ALT) + mods |= GRUB_TERM_STATUS_LALT; + if (data0 & GRUB_USB_KEYBOARD_RIGHT_ALT) + mods |= GRUB_TERM_STATUS_RALT; + + return mods; +} + +static void +grub_usb_keyboard_detach (grub_usb_device_t usbdev, + int config __attribute__ ((unused)), + int interface __attribute__ ((unused))) +{ + unsigned i; + for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++) + { + struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data; + + if (!data) + continue; + + if (data->usbdev != usbdev) + continue; + + if (data->transfer) + grub_usb_cancel_transfer (data->transfer); + + grub_term_unregister_input (&grub_usb_keyboards[i]); + grub_free ((char *) grub_usb_keyboards[i].name); + grub_usb_keyboards[i].name = NULL; + grub_free (grub_usb_keyboards[i].data); + grub_usb_keyboards[i].data = 0; + } +} + +static int +grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno) +{ + unsigned curnum; + struct grub_usb_keyboard_data *data; + struct grub_usb_desc_endp *endp = NULL; + int j; + + grub_dprintf ("usb_keyboard", "%x %x %x %d %d\n", + usbdev->descdev.class, usbdev->descdev.subclass, + usbdev->descdev.protocol, configno, interfno); + + for (curnum = 0; curnum < ARRAY_SIZE (grub_usb_keyboards); curnum++) + if (!grub_usb_keyboards[curnum].data) + break; + + if (curnum == ARRAY_SIZE (grub_usb_keyboards)) + return 0; + + if (usbdev->descdev.class != 0 + || usbdev->descdev.subclass != 0 || usbdev->descdev.protocol != 0) + return 0; + + if (usbdev->config[configno].interf[interfno].descif->subclass + != USB_HID_BOOT_SUBCLASS + || usbdev->config[configno].interf[interfno].descif->protocol + != USB_HID_KBD_PROTOCOL) + return 0; + + for (j = 0; j < usbdev->config[configno].interf[interfno].descif->endpointcnt; + j++) + { + endp = &usbdev->config[configno].interf[interfno].descendp[j]; + + if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp) + == GRUB_USB_EP_INTERRUPT) + break; + } + if (j == usbdev->config[configno].interf[interfno].descif->endpointcnt) + return 0; + + grub_dprintf ("usb_keyboard", "HID found!\n"); + + data = grub_malloc (sizeof (*data)); + if (!data) + { + grub_print_error (); + return 0; + } + + data->usbdev = usbdev; + data->interfno = interfno; + data->endp = endp; + + /* Configure device */ + grub_usb_set_configuration (usbdev, configno + 1); + + /* Place the device in boot mode. */ + grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, + USB_HID_SET_PROTOCOL, 0, interfno, 0, 0); + + /* Reports every time an event occurs and not more often than that. */ + grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, + USB_HID_SET_IDLE, 0<<8, interfno, 0, 0); + + grub_memcpy (&grub_usb_keyboards[curnum], &grub_usb_keyboard_term, + sizeof (grub_usb_keyboards[curnum])); + grub_usb_keyboards[curnum].data = data; + usbdev->config[configno].interf[interfno].detach_hook + = grub_usb_keyboard_detach; + grub_usb_keyboards[curnum].name = grub_xasprintf ("usb_keyboard%d", curnum); + if (!grub_usb_keyboards[curnum].name) + { + grub_print_error (); + return 0; + } + + /* Test showed that getting report may make the keyboard go nuts. + Moreover since we're reattaching keyboard it usually sends + an initial message on interrupt pipe and so we retrieve + the same keystatus. + */ +#if 0 + { + grub_uint8_t report[8]; + grub_usb_err_t err; + grub_memset (report, 0, sizeof (report)); + err = grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_IN, + USB_HID_GET_REPORT, 0x0100, interfno, + sizeof (report), (char *) report); + if (err) + data->status = 0; + else + data->status = report[0]; + } +#else + data->status = 0; +#endif + + data->transfer = grub_usb_bulk_read_background (usbdev, + data->endp, + sizeof (data->report), + (char *) data->report); + if (!data->transfer) + { + grub_print_error (); + return 0; + } + + data->last_key = -1; + data->mods = 0; + data->dead = 0; + + grub_term_register_input_active ("usb_keyboard", &grub_usb_keyboards[curnum]); + + return 1; +} + + + +static void +send_leds (struct grub_usb_keyboard_data *termdata) +{ + char report[1]; + report[0] = 0; + if (termdata->mods & GRUB_TERM_STATUS_CAPS) + report[0] |= LED_CAPS_LOCK; + if (termdata->mods & GRUB_TERM_STATUS_NUM) + report[0] |= LED_NUM_LOCK; + grub_usb_control_msg (termdata->usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, + USB_HID_SET_REPORT, 0x0200, termdata->interfno, + sizeof (report), (char *) report); + grub_errno = GRUB_ERR_NONE; +} + +static int +parse_keycode (struct grub_usb_keyboard_data *termdata) +{ + int index = termdata->index; + int i, keycode; + + /* Sanity check */ + if (index < 2) + index = 2; + + for ( ; index < termdata->max_index; index++) + { + keycode = termdata->current_report[index]; + + if (keycode == KEY_NO_KEY + || keycode == KEY_ERR_BUFFER + || keycode == KEY_ERR_POST + || keycode == KEY_ERR_UNDEF) + { + /* Don't parse (rest of) this report */ + termdata->index = 0; + if (keycode != KEY_NO_KEY) + /* Don't replace last report with current faulty report + * in future ! */ + grub_memcpy (termdata->current_report, + termdata->last_report, + sizeof (termdata->report)); + return GRUB_TERM_NO_KEY; + } + + /* Try to find current keycode in last report. */ + for (i = 2; i < 8; i++) + if (keycode == termdata->last_report[i]) + break; + if (i < 8) + /* Keycode is in last report, it means it was not released, + * ignore it. */ + continue; + + if (keycode == KEY_CAPS_LOCK) + { + termdata->mods ^= GRUB_TERM_STATUS_CAPS; + send_leds (termdata); + continue; + } + + if (keycode == KEY_NUM_LOCK) + { + termdata->mods ^= GRUB_TERM_STATUS_NUM; + send_leds (termdata); + continue; + } + + termdata->last_key = grub_term_map_key (keycode, + interpret_status (termdata->current_report[0]) + | termdata->mods); + termdata->repeat_time = grub_get_time_ms () + GRUB_TERM_REPEAT_PRE_INTERVAL; + + grub_errno = GRUB_ERR_NONE; + + index++; + if (index >= termdata->max_index) + termdata->index = 0; + else + termdata->index = index; + + return termdata->last_key; + } + + /* All keycodes parsed */ + termdata->index = 0; + return GRUB_TERM_NO_KEY; +} + +static int +grub_usb_keyboard_getkey (struct grub_term_input *term) +{ + grub_usb_err_t err; + struct grub_usb_keyboard_data *termdata = term->data; + grub_size_t actual; + int keycode = GRUB_TERM_NO_KEY; + + if (termdata->dead) + return GRUB_TERM_NO_KEY; + + if (termdata->index) + keycode = parse_keycode (termdata); + if (keycode != GRUB_TERM_NO_KEY) + return keycode; + + /* Poll interrupt pipe. */ + err = grub_usb_check_transfer (termdata->transfer, &actual); + + if (err == GRUB_USB_ERR_WAIT) + { + if (termdata->last_key != -1 + && grub_get_time_ms () > termdata->repeat_time) + { + termdata->repeat_time = grub_get_time_ms () + + GRUB_TERM_REPEAT_INTERVAL; + return termdata->last_key; + } + return GRUB_TERM_NO_KEY; + } + + if (!err && (actual >= 3)) + grub_memcpy (termdata->last_report, + termdata->current_report, + sizeof (termdata->report)); + + grub_memcpy (termdata->current_report, + termdata->report, + sizeof (termdata->report)); + + termdata->transfer = grub_usb_bulk_read_background (termdata->usbdev, + termdata->endp, + sizeof (termdata->report), + (char *) termdata->report); + if (!termdata->transfer) + { + grub_printf ("%s failed. Stopped\n", term->name); + termdata->dead = 1; + } + + termdata->last_key = -1; + + grub_dprintf ("usb_keyboard", + "err = %d, actual = %" PRIuGRUB_SIZE + " report: 0x%02x 0x%02x 0x%02x 0x%02x" + " 0x%02x 0x%02x 0x%02x 0x%02x\n", + err, actual, + termdata->current_report[0], termdata->current_report[1], + termdata->current_report[2], termdata->current_report[3], + termdata->current_report[4], termdata->current_report[5], + termdata->current_report[6], termdata->current_report[7]); + + if (err || actual < 1) + return GRUB_TERM_NO_KEY; + + termdata->status = termdata->current_report[0]; + + if (actual < 3) + return GRUB_TERM_NO_KEY; + + termdata->index = 2; /* New data received. */ + termdata->max_index = actual; + + return parse_keycode (termdata); +} + +static int +grub_usb_keyboard_getkeystatus (struct grub_term_input *term) +{ + struct grub_usb_keyboard_data *termdata = term->data; + + return interpret_status (termdata->status) | termdata->mods; +} + +static struct grub_usb_attach_desc attach_hook = +{ + .class = GRUB_USB_CLASS_HID, + .hook = grub_usb_keyboard_attach +}; + +GRUB_MOD_INIT(usb_keyboard) +{ + grub_usb_register_attach_hook_class (&attach_hook); +} + +GRUB_MOD_FINI(usb_keyboard) +{ + unsigned i; + for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++) + if (grub_usb_keyboards[i].data) + { + struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data; + + if (!data) + continue; + + if (data->transfer) + grub_usb_cancel_transfer (data->transfer); + + grub_term_unregister_input (&grub_usb_keyboards[i]); + grub_free ((char *) grub_usb_keyboards[i].name); + grub_usb_keyboards[i].name = NULL; + grub_free (grub_usb_keyboards[i].data); + grub_usb_keyboards[i].data = 0; + } + grub_usb_unregister_attach_hook_class (&attach_hook); +} diff --git a/grub-core/term/xen/console.c b/grub-core/term/xen/console.c new file mode 100644 index 0000000..a1f15f7 --- /dev/null +++ b/grub-core/term/xen/console.c @@ -0,0 +1,122 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +static int +readkey (struct grub_term_input *term __attribute__ ((unused))) +{ + grub_size_t prod, cons; + int r; + mb (); + prod = grub_xen_xcons->in_prod; + cons = grub_xen_xcons->in_cons; + if (prod <= cons) + return -1; + r = grub_xen_xcons->in[cons]; + cons++; + mb (); + grub_xen_xcons->in_cons = cons; + return r; +} + +static int signal_sent = 1; + +static void +refresh (struct grub_term_output *term __attribute__ ((unused))) +{ + struct evtchn_send send; + send.port = grub_xen_start_page_addr->console.domU.evtchn; + grub_xen_event_channel_op (EVTCHNOP_send, &send); + signal_sent = 1; + while (grub_xen_xcons->out_prod != grub_xen_xcons->out_cons) + { + grub_xen_sched_op (SCHEDOP_yield, 0); + } +} + +static void +put (struct grub_term_output *term __attribute__ ((unused)), const int c) +{ + grub_size_t prod, cons; + + while (1) + { + mb (); + prod = grub_xen_xcons->out_prod; + cons = grub_xen_xcons->out_cons; + if (prod < cons + sizeof (grub_xen_xcons->out)) + break; + if (!signal_sent) + refresh (term); + grub_xen_sched_op (SCHEDOP_yield, 0); + } + grub_xen_xcons->out[prod++ & (sizeof (grub_xen_xcons->out) - 1)] = c; + mb (); + grub_xen_xcons->out_prod = prod; + signal_sent = 0; +} + + +struct grub_terminfo_input_state grub_console_terminfo_input = { + .readkey = readkey +}; + +struct grub_terminfo_output_state grub_console_terminfo_output = { + .put = put, + .size = {80, 24} +}; + +static struct grub_term_input grub_console_term_input = { + .name = "console", + .init = 0, + .getkey = grub_terminfo_getkey, + .data = &grub_console_terminfo_input +}; + +static struct grub_term_output grub_console_term_output = { + .name = "console", + .init = 0, + .putchar = grub_terminfo_putchar, + .getxy = grub_terminfo_getxy, + .getwh = grub_terminfo_getwh, + .gotoxy = grub_terminfo_gotoxy, + .cls = grub_terminfo_cls, + .refresh = refresh, + .setcolorstate = grub_terminfo_setcolorstate, + .setcursor = grub_terminfo_setcursor, + .flags = GRUB_TERM_CODE_TYPE_ASCII, + .data = &grub_console_terminfo_output, +}; + + +void +grub_console_init (void) +{ + grub_term_register_input ("console", &grub_console_term_input); + grub_term_register_output ("console", &grub_console_term_output); + + grub_terminfo_init (); + grub_terminfo_output_register (&grub_console_term_output, "vt100-color"); +} -- cgit v1.2.3