uboot/lib/efi_loader/efi_console.c
<<
>>
Prefs
   1/*
   2 *  EFI application console interface
   3 *
   4 *  Copyright (c) 2016 Alexander Graf
   5 *
   6 *  SPDX-License-Identifier:     GPL-2.0+
   7 */
   8
   9#include <common.h>
  10#include <efi_loader.h>
  11
  12/* If we can't determine the console size, default to 80x24 */
  13static int console_columns = 80;
  14static int console_rows = 24;
  15static bool console_size_queried;
  16
  17const efi_guid_t efi_guid_console_control = CONSOLE_CONTROL_GUID;
  18
  19#define cESC '\x1b'
  20#define ESC "\x1b"
  21
  22static efi_status_t EFIAPI efi_cin_get_mode(
  23                        struct efi_console_control_protocol *this,
  24                        int *mode, char *uga_exists, char *std_in_locked)
  25{
  26        EFI_ENTRY("%p, %p, %p, %p", this, mode, uga_exists, std_in_locked);
  27
  28        if (mode)
  29                *mode = EFI_CONSOLE_MODE_TEXT;
  30        if (uga_exists)
  31                *uga_exists = 0;
  32        if (std_in_locked)
  33                *std_in_locked = 0;
  34
  35        return EFI_EXIT(EFI_SUCCESS);
  36}
  37
  38static efi_status_t EFIAPI efi_cin_set_mode(
  39                        struct efi_console_control_protocol *this, int mode)
  40{
  41        EFI_ENTRY("%p, %d", this, mode);
  42        return EFI_EXIT(EFI_UNSUPPORTED);
  43}
  44
  45static efi_status_t EFIAPI efi_cin_lock_std_in(
  46                        struct efi_console_control_protocol *this,
  47                        uint16_t *password)
  48{
  49        EFI_ENTRY("%p, %p", this, password);
  50        return EFI_EXIT(EFI_UNSUPPORTED);
  51}
  52
  53const struct efi_console_control_protocol efi_console_control = {
  54        .get_mode = efi_cin_get_mode,
  55        .set_mode = efi_cin_set_mode,
  56        .lock_std_in = efi_cin_lock_std_in,
  57};
  58
  59static struct simple_text_output_mode efi_con_mode = {
  60        .max_mode = 0,
  61        .mode = 0,
  62        .attribute = 0,
  63        .cursor_column = 0,
  64        .cursor_row = 0,
  65        .cursor_visible = 1,
  66};
  67
  68static int term_read_reply(int *n, int maxnum, char end_char)
  69{
  70        char c;
  71        int i = 0;
  72
  73        c = getc();
  74        if (c != cESC)
  75                return -1;
  76        c = getc();
  77        if (c != '[')
  78                return -1;
  79
  80        n[0] = 0;
  81        while (1) {
  82                c = getc();
  83                if (c == ';') {
  84                        i++;
  85                        if (i >= maxnum)
  86                                return -1;
  87                        n[i] = 0;
  88                        continue;
  89                } else if (c == end_char) {
  90                        break;
  91                } else if (c > '9' || c < '0') {
  92                        return -1;
  93                }
  94
  95                /* Read one more decimal position */
  96                n[i] *= 10;
  97                n[i] += c - '0';
  98        }
  99
 100        return 0;
 101}
 102
 103static efi_status_t EFIAPI efi_cout_reset(
 104                        struct efi_simple_text_output_protocol *this,
 105                        char extended_verification)
 106{
 107        EFI_ENTRY("%p, %d", this, extended_verification);
 108        return EFI_EXIT(EFI_UNSUPPORTED);
 109}
 110
 111static void print_unicode_in_utf8(u16 c)
 112{
 113        char utf8[4] = { 0 };
 114        char *b = utf8;
 115
 116        if (c < 0x80) {
 117                *(b++) = c;
 118        } else if (c < 0x800) {
 119                *(b++) = 192 + c / 64;
 120                *(b++) = 128 + c % 64;
 121        } else {
 122                *(b++) = 224 + c / 4096;
 123                *(b++) = 128 + c / 64 % 64;
 124                *(b++) = 128 + c % 64;
 125        }
 126
 127        puts(utf8);
 128}
 129
 130static efi_status_t EFIAPI efi_cout_output_string(
 131                        struct efi_simple_text_output_protocol *this,
 132                        const unsigned short *string)
 133{
 134        u16 ch;
 135
 136        EFI_ENTRY("%p, %p", this, string);
 137        for (;(ch = *string); string++) {
 138                print_unicode_in_utf8(ch);
 139                efi_con_mode.cursor_column++;
 140                if (ch == '\n') {
 141                        efi_con_mode.cursor_column = 1;
 142                        efi_con_mode.cursor_row++;
 143                } else if (efi_con_mode.cursor_column > console_columns) {
 144                        efi_con_mode.cursor_column = 1;
 145                        efi_con_mode.cursor_row++;
 146                }
 147                if (efi_con_mode.cursor_row > console_rows) {
 148                        efi_con_mode.cursor_row = console_rows;
 149                }
 150        }
 151
 152        return EFI_EXIT(EFI_SUCCESS);
 153}
 154
 155static efi_status_t EFIAPI efi_cout_test_string(
 156                        struct efi_simple_text_output_protocol *this,
 157                        const unsigned short *string)
 158{
 159        EFI_ENTRY("%p, %p", this, string);
 160        return EFI_EXIT(EFI_SUCCESS);
 161}
 162
 163static efi_status_t EFIAPI efi_cout_query_mode(
 164                        struct efi_simple_text_output_protocol *this,
 165                        unsigned long mode_number, unsigned long *columns,
 166                        unsigned long *rows)
 167{
 168        EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows);
 169
 170        if (!console_size_queried) {
 171                /* Ask the terminal about its size */
 172                int n[3];
 173                u64 timeout;
 174
 175                console_size_queried = true;
 176
 177                /* Empty input buffer */
 178                while (tstc())
 179                        getc();
 180
 181                printf(ESC"[18t");
 182
 183                /* Check if we have a terminal that understands */
 184                timeout = timer_get_us() + 1000000;
 185                while (!tstc())
 186                        if (timer_get_us() > timeout)
 187                                goto out;
 188
 189                /* Read {depth,rows,cols} */
 190                if (term_read_reply(n, 3, 't')) {
 191                        goto out;
 192                }
 193
 194                console_columns = n[2];
 195                console_rows = n[1];
 196        }
 197
 198out:
 199        if (columns)
 200                *columns = console_columns;
 201        if (rows)
 202                *rows = console_rows;
 203
 204        return EFI_EXIT(EFI_SUCCESS);
 205}
 206
 207static efi_status_t EFIAPI efi_cout_set_mode(
 208                        struct efi_simple_text_output_protocol *this,
 209                        unsigned long mode_number)
 210{
 211        EFI_ENTRY("%p, %ld", this, mode_number);
 212
 213        /* We only support text output for now */
 214        if (mode_number == EFI_CONSOLE_MODE_TEXT)
 215                return EFI_EXIT(EFI_SUCCESS);
 216
 217        return EFI_EXIT(EFI_UNSUPPORTED);
 218}
 219
 220static efi_status_t EFIAPI efi_cout_set_attribute(
 221                        struct efi_simple_text_output_protocol *this,
 222                        unsigned long attribute)
 223{
 224        EFI_ENTRY("%p, %lx", this, attribute);
 225
 226        /* Just ignore attributes (colors) for now */
 227        return EFI_EXIT(EFI_UNSUPPORTED);
 228}
 229
 230static efi_status_t EFIAPI efi_cout_clear_screen(
 231                        struct efi_simple_text_output_protocol *this)
 232{
 233        EFI_ENTRY("%p", this);
 234
 235        printf(ESC"[2J");
 236
 237        return EFI_EXIT(EFI_SUCCESS);
 238}
 239
 240static efi_status_t EFIAPI efi_cout_set_cursor_position(
 241                        struct efi_simple_text_output_protocol *this,
 242                        unsigned long column, unsigned long row)
 243{
 244        EFI_ENTRY("%p, %ld, %ld", this, column, row);
 245
 246        printf(ESC"[%d;%df", (int)row, (int)column);
 247        efi_con_mode.cursor_column = column;
 248        efi_con_mode.cursor_row = row;
 249
 250        return EFI_EXIT(EFI_SUCCESS);
 251}
 252
 253static efi_status_t EFIAPI efi_cout_enable_cursor(
 254                        struct efi_simple_text_output_protocol *this,
 255                        bool enable)
 256{
 257        EFI_ENTRY("%p, %d", this, enable);
 258
 259        printf(ESC"[?25%c", enable ? 'h' : 'l');
 260
 261        return EFI_EXIT(EFI_SUCCESS);
 262}
 263
 264const struct efi_simple_text_output_protocol efi_con_out = {
 265        .reset = efi_cout_reset,
 266        .output_string = efi_cout_output_string,
 267        .test_string = efi_cout_test_string,
 268        .query_mode = efi_cout_query_mode,
 269        .set_mode = efi_cout_set_mode,
 270        .set_attribute = efi_cout_set_attribute,
 271        .clear_screen = efi_cout_clear_screen,
 272        .set_cursor_position = efi_cout_set_cursor_position,
 273        .enable_cursor = efi_cout_enable_cursor,
 274        .mode = (void*)&efi_con_mode,
 275};
 276
 277static efi_status_t EFIAPI efi_cin_reset(
 278                        struct efi_simple_input_interface *this,
 279                        bool extended_verification)
 280{
 281        EFI_ENTRY("%p, %d", this, extended_verification);
 282        return EFI_EXIT(EFI_UNSUPPORTED);
 283}
 284
 285static efi_status_t EFIAPI efi_cin_read_key_stroke(
 286                        struct efi_simple_input_interface *this,
 287                        struct efi_input_key *key)
 288{
 289        struct efi_input_key pressed_key = {
 290                .scan_code = 0,
 291                .unicode_char = 0,
 292        };
 293        char ch;
 294
 295        EFI_ENTRY("%p, %p", this, key);
 296
 297        /* We don't do interrupts, so check for timers cooperatively */
 298        efi_timer_check();
 299
 300        if (!tstc()) {
 301                /* No key pressed */
 302                return EFI_EXIT(EFI_NOT_READY);
 303        }
 304
 305        ch = getc();
 306        if (ch == cESC) {
 307                /* Escape Sequence */
 308                ch = getc();
 309                switch (ch) {
 310                case cESC: /* ESC */
 311                        pressed_key.scan_code = 23;
 312                        break;
 313                case 'O': /* F1 - F4 */
 314                        pressed_key.scan_code = getc() - 'P' + 11;
 315                        break;
 316                case 'a'...'z':
 317                        ch = ch - 'a';
 318                        break;
 319                case '[':
 320                        ch = getc();
 321                        switch (ch) {
 322                        case 'A'...'D': /* up, down right, left */
 323                                pressed_key.scan_code = ch - 'A' + 1;
 324                                break;
 325                        case 'F': /* End */
 326                                pressed_key.scan_code = 6;
 327                                break;
 328                        case 'H': /* Home */
 329                                pressed_key.scan_code = 5;
 330                                break;
 331                        case '1': /* F5 - F8 */
 332                                pressed_key.scan_code = getc() - '0' + 11;
 333                                getc();
 334                                break;
 335                        case '2': /* F9 - F12 */
 336                                pressed_key.scan_code = getc() - '0' + 19;
 337                                getc();
 338                                break;
 339                        case '3': /* DEL */
 340                                pressed_key.scan_code = 8;
 341                                getc();
 342                                break;
 343                        }
 344                        break;
 345                }
 346        } else if (ch == 0x7f) {
 347                /* Backspace */
 348                ch = 0x08;
 349        }
 350        pressed_key.unicode_char = ch;
 351        *key = pressed_key;
 352
 353        return EFI_EXIT(EFI_SUCCESS);
 354}
 355
 356const struct efi_simple_input_interface efi_con_in = {
 357        .reset = efi_cin_reset,
 358        .read_key_stroke = efi_cin_read_key_stroke,
 359        .wait_for_key = NULL,
 360};
 361