Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/efi/libefi/efi_console.c
34878 views
1
/*-
2
* Copyright (c) 2000 Doug Rabson
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <sys/param.h>
28
#include <efi.h>
29
#include <efilib.h>
30
#include <teken.h>
31
#include <sys/reboot.h>
32
#include <machine/metadata.h>
33
#include <gfx_fb.h>
34
#include <framebuffer.h>
35
#include "bootstrap.h"
36
37
extern EFI_GUID gop_guid;
38
39
bool boot_services_active = true; /* boot services active first thing in main */
40
41
static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
42
static SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
43
static SIMPLE_INPUT_INTERFACE *conin;
44
static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex;
45
static bool efi_started;
46
static int mode; /* Does ConOut have serial console? */
47
48
static uint32_t utf8_left;
49
static uint32_t utf8_partial;
50
#ifdef TERM_EMU
51
#define DEFAULT_FGCOLOR EFI_LIGHTGRAY
52
#define DEFAULT_BGCOLOR EFI_BLACK
53
54
#define MAXARGS 8
55
static int args[MAXARGS], argc;
56
static int fg_c, bg_c, curx, cury;
57
static int esc;
58
59
void get_pos(int *x, int *y);
60
void curs_move(int *_x, int *_y, int x, int y);
61
static void CL(int);
62
void HO(void);
63
void end_term(void);
64
#endif
65
66
#define TEXT_ROWS 25
67
#define TEXT_COLS 80
68
69
static tf_bell_t efi_cons_bell;
70
static tf_cursor_t efi_text_cursor;
71
static tf_putchar_t efi_text_putchar;
72
static tf_fill_t efi_text_fill;
73
static tf_copy_t efi_text_copy;
74
static tf_param_t efi_text_param;
75
static tf_respond_t efi_cons_respond;
76
77
static teken_funcs_t tf = {
78
.tf_bell = efi_cons_bell,
79
.tf_cursor = efi_text_cursor,
80
.tf_putchar = efi_text_putchar,
81
.tf_fill = efi_text_fill,
82
.tf_copy = efi_text_copy,
83
.tf_param = efi_text_param,
84
.tf_respond = efi_cons_respond,
85
};
86
87
static teken_funcs_t tfx = {
88
.tf_bell = efi_cons_bell,
89
.tf_cursor = gfx_fb_cursor,
90
.tf_putchar = gfx_fb_putchar,
91
.tf_fill = gfx_fb_fill,
92
.tf_copy = gfx_fb_copy,
93
.tf_param = gfx_fb_param,
94
.tf_respond = efi_cons_respond,
95
};
96
97
#define KEYBUFSZ 10
98
static unsigned keybuf[KEYBUFSZ]; /* keybuf for extended codes */
99
static int key_pending;
100
101
static const unsigned char teken_color_to_efi_color[16] = {
102
EFI_BLACK,
103
EFI_RED,
104
EFI_GREEN,
105
EFI_BROWN,
106
EFI_BLUE,
107
EFI_MAGENTA,
108
EFI_CYAN,
109
EFI_LIGHTGRAY,
110
EFI_DARKGRAY,
111
EFI_LIGHTRED,
112
EFI_LIGHTGREEN,
113
EFI_YELLOW,
114
EFI_LIGHTBLUE,
115
EFI_LIGHTMAGENTA,
116
EFI_LIGHTCYAN,
117
EFI_WHITE
118
};
119
120
static void efi_cons_probe(struct console *);
121
static int efi_cons_init(int);
122
void efi_cons_putchar(int);
123
int efi_cons_getchar(void);
124
void efi_cons_efiputchar(int);
125
int efi_cons_poll(void);
126
static void cons_draw_frame(teken_attr_t *);
127
128
struct console efi_console = {
129
.c_name = "efi",
130
.c_desc = "EFI console",
131
.c_flags = C_WIDEOUT,
132
.c_probe = efi_cons_probe,
133
.c_init = efi_cons_init,
134
.c_out = efi_cons_putchar,
135
.c_in = efi_cons_getchar,
136
.c_ready = efi_cons_poll
137
};
138
139
/*
140
* This function is used to mark a rectangular image area so the scrolling
141
* will know we need to copy the data from there.
142
*/
143
void
144
term_image_display(teken_gfx_t *state, const teken_rect_t *r)
145
{
146
teken_pos_t p;
147
int idx;
148
149
if (screen_buffer == NULL)
150
return;
151
152
for (p.tp_row = r->tr_begin.tp_row;
153
p.tp_row < r->tr_end.tp_row; p.tp_row++) {
154
for (p.tp_col = r->tr_begin.tp_col;
155
p.tp_col < r->tr_end.tp_col; p.tp_col++) {
156
idx = p.tp_col + p.tp_row * state->tg_tp.tp_col;
157
if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
158
return;
159
screen_buffer[idx].a.ta_format |= TF_IMAGE;
160
}
161
}
162
}
163
164
/*
165
* Not implemented.
166
*/
167
static void
168
efi_cons_bell(void *s __unused)
169
{
170
}
171
172
static void
173
efi_text_cursor(void *arg, const teken_pos_t *p)
174
{
175
teken_gfx_t *state = arg;
176
UINTN col, row;
177
178
if (!boot_services_active)
179
return;
180
181
row = p->tp_row;
182
if (p->tp_row >= state->tg_tp.tp_row)
183
row = state->tg_tp.tp_row - 1;
184
185
col = p->tp_col;
186
if (p->tp_col >= state->tg_tp.tp_col)
187
col = state->tg_tp.tp_col - 1;
188
189
conout->SetCursorPosition(conout, col, row);
190
}
191
192
static void
193
efi_text_printchar(teken_gfx_t *state, const teken_pos_t *p, bool autoscroll)
194
{
195
UINTN a, attr;
196
struct text_pixel *px;
197
teken_color_t fg, bg, tmp;
198
199
px = screen_buffer + p->tp_col + p->tp_row * state->tg_tp.tp_col;
200
a = conout->Mode->Attribute;
201
202
fg = teken_256to16(px->a.ta_fgcolor);
203
bg = teken_256to16(px->a.ta_bgcolor);
204
if (px->a.ta_format & TF_BOLD)
205
fg |= TC_LIGHT;
206
if (px->a.ta_format & TF_BLINK)
207
bg |= TC_LIGHT;
208
209
if (px->a.ta_format & TF_REVERSE) {
210
tmp = fg;
211
fg = bg;
212
bg = tmp;
213
}
214
215
attr = EFI_TEXT_ATTR(teken_color_to_efi_color[fg],
216
teken_color_to_efi_color[bg] & 0x7);
217
218
conout->SetCursorPosition(conout, p->tp_col, p->tp_row);
219
220
/* to prevent autoscroll, skip print of lower right char */
221
if (!autoscroll &&
222
p->tp_row == state->tg_tp.tp_row - 1 &&
223
p->tp_col == state->tg_tp.tp_col - 1)
224
return;
225
226
(void) conout->SetAttribute(conout, attr);
227
efi_cons_efiputchar(px->c);
228
(void) conout->SetAttribute(conout, a);
229
}
230
231
static void
232
efi_text_putchar(void *s, const teken_pos_t *p, teken_char_t c,
233
const teken_attr_t *a)
234
{
235
teken_gfx_t *state = s;
236
EFI_STATUS status;
237
int idx;
238
239
if (!boot_services_active)
240
return;
241
242
idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
243
if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
244
return;
245
246
screen_buffer[idx].c = c;
247
screen_buffer[idx].a = *a;
248
249
efi_text_printchar(s, p, false);
250
}
251
252
static void
253
efi_text_fill(void *arg, const teken_rect_t *r, teken_char_t c,
254
const teken_attr_t *a)
255
{
256
teken_gfx_t *state = arg;
257
teken_pos_t p;
258
259
if (!boot_services_active)
260
return;
261
262
if (state->tg_cursor_visible)
263
conout->EnableCursor(conout, FALSE);
264
for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
265
p.tp_row++)
266
for (p.tp_col = r->tr_begin.tp_col;
267
p.tp_col < r->tr_end.tp_col; p.tp_col++)
268
efi_text_putchar(state, &p, c, a);
269
if (state->tg_cursor_visible)
270
conout->EnableCursor(conout, TRUE);
271
}
272
273
static void
274
efi_text_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s,
275
teken_pos_t *d, bool scroll)
276
{
277
unsigned soffset, doffset;
278
teken_pos_t sp, dp;
279
int x;
280
281
soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col;
282
doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col;
283
284
sp = *s;
285
dp = *d;
286
for (x = 0; x < ncol; x++) {
287
sp.tp_col = s->tp_col + x;
288
dp.tp_col = d->tp_col + x;
289
if (!is_same_pixel(&screen_buffer[soffset + x],
290
&screen_buffer[doffset + x])) {
291
screen_buffer[doffset + x] =
292
screen_buffer[soffset + x];
293
if (!scroll)
294
efi_text_printchar(state, &dp, false);
295
} else if (scroll) {
296
/* Draw last char and trigger scroll. */
297
if (dp.tp_col + 1 == state->tg_tp.tp_col &&
298
dp.tp_row + 1 == state->tg_tp.tp_row) {
299
efi_text_printchar(state, &dp, true);
300
}
301
}
302
}
303
}
304
305
static void
306
efi_text_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p)
307
{
308
teken_gfx_t *state = arg;
309
unsigned doffset, soffset;
310
teken_pos_t d, s;
311
int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */
312
bool scroll = false;
313
314
if (!boot_services_active)
315
return;
316
317
/*
318
* Copying is a little tricky. We must make sure we do it in
319
* correct order, to make sure we don't overwrite our own data.
320
*/
321
322
nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
323
ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
324
325
/*
326
* Check if we do copy whole screen.
327
*/
328
if (p->tp_row == 0 && p->tp_col == 0 &&
329
nrow == state->tg_tp.tp_row - 2 && ncol == state->tg_tp.tp_col - 2)
330
scroll = true;
331
332
soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col;
333
doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col;
334
335
/* remove the cursor */
336
if (state->tg_cursor_visible)
337
conout->EnableCursor(conout, FALSE);
338
339
/*
340
* Copy line by line.
341
*/
342
if (doffset <= soffset) {
343
s = r->tr_begin;
344
d = *p;
345
for (y = 0; y < nrow; y++) {
346
s.tp_row = r->tr_begin.tp_row + y;
347
d.tp_row = p->tp_row + y;
348
349
efi_text_copy_line(state, ncol, &s, &d, scroll);
350
}
351
} else {
352
for (y = nrow - 1; y >= 0; y--) {
353
s.tp_row = r->tr_begin.tp_row + y;
354
d.tp_row = p->tp_row + y;
355
356
efi_text_copy_line(state, ncol, &s, &d, false);
357
}
358
}
359
360
/* display the cursor */
361
if (state->tg_cursor_visible)
362
conout->EnableCursor(conout, TRUE);
363
}
364
365
static void
366
efi_text_param(void *arg, int cmd, unsigned int value)
367
{
368
teken_gfx_t *state = arg;
369
370
if (!boot_services_active)
371
return;
372
373
switch (cmd) {
374
case TP_SETLOCALCURSOR:
375
/*
376
* 0 means normal (usually block), 1 means hidden, and
377
* 2 means blinking (always block) for compatibility with
378
* syscons. We don't support any changes except hiding,
379
* so must map 2 to 0.
380
*/
381
value = (value == 1) ? 0 : 1;
382
/* FALLTHROUGH */
383
case TP_SHOWCURSOR:
384
if (value != 0) {
385
conout->EnableCursor(conout, TRUE);
386
state->tg_cursor_visible = true;
387
} else {
388
conout->EnableCursor(conout, FALSE);
389
state->tg_cursor_visible = false;
390
}
391
break;
392
default:
393
/* Not yet implemented */
394
break;
395
}
396
}
397
398
/*
399
* Not implemented.
400
*/
401
static void
402
efi_cons_respond(void *s __unused, const void *buf __unused,
403
size_t len __unused)
404
{
405
}
406
407
/*
408
* Set up conin/conout/coninex to make sure we have input ready.
409
*/
410
static void
411
efi_cons_probe(struct console *cp)
412
{
413
EFI_STATUS status;
414
415
conout = ST->ConOut;
416
conin = ST->ConIn;
417
418
/*
419
* Call SetMode to work around buggy firmware.
420
*/
421
status = conout->SetMode(conout, conout->Mode->Mode);
422
423
if (coninex == NULL) {
424
status = BS->OpenProtocol(ST->ConsoleInHandle,
425
&simple_input_ex_guid, (void **)&coninex,
426
IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
427
if (status != EFI_SUCCESS)
428
coninex = NULL;
429
}
430
431
cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
432
}
433
434
static bool
435
color_name_to_teken(const char *name, int *val)
436
{
437
int light = 0;
438
if (strncasecmp(name, "light", 5) == 0) {
439
name += 5;
440
light = TC_LIGHT;
441
} else if (strncasecmp(name, "bright", 6) == 0) {
442
name += 6;
443
light = TC_LIGHT;
444
}
445
if (strcasecmp(name, "black") == 0) {
446
*val = TC_BLACK | light;
447
return (true);
448
}
449
if (strcasecmp(name, "red") == 0) {
450
*val = TC_RED | light;
451
return (true);
452
}
453
if (strcasecmp(name, "green") == 0) {
454
*val = TC_GREEN | light;
455
return (true);
456
}
457
if (strcasecmp(name, "yellow") == 0 || strcasecmp(name, "brown") == 0) {
458
*val = TC_YELLOW | light;
459
return (true);
460
}
461
if (strcasecmp(name, "blue") == 0) {
462
*val = TC_BLUE | light;
463
return (true);
464
}
465
if (strcasecmp(name, "magenta") == 0) {
466
*val = TC_MAGENTA | light;
467
return (true);
468
}
469
if (strcasecmp(name, "cyan") == 0) {
470
*val = TC_CYAN | light;
471
return (true);
472
}
473
if (strcasecmp(name, "white") == 0) {
474
*val = TC_WHITE | light;
475
return (true);
476
}
477
return (false);
478
}
479
480
static int
481
efi_set_colors(struct env_var *ev, int flags, const void *value)
482
{
483
int val = 0;
484
char buf[3];
485
const void *evalue;
486
const teken_attr_t *ap;
487
teken_attr_t a;
488
489
if (value == NULL)
490
return (CMD_OK);
491
492
if (color_name_to_teken(value, &val)) {
493
snprintf(buf, sizeof (buf), "%d", val);
494
evalue = buf;
495
} else {
496
char *end;
497
long lval;
498
499
errno = 0;
500
lval = strtol(value, &end, 0);
501
if (errno != 0 || *end != '\0' || lval < 0 || lval > 15) {
502
printf("Allowed values are either ansi color name or "
503
"number from range [0-15].\n");
504
return (CMD_OK);
505
}
506
val = (int)lval;
507
evalue = value;
508
}
509
510
ap = teken_get_defattr(&gfx_state.tg_teken);
511
a = *ap;
512
if (strcmp(ev->ev_name, "teken.fg_color") == 0) {
513
/* is it already set? */
514
if (ap->ta_fgcolor == val)
515
return (CMD_OK);
516
a.ta_fgcolor = val;
517
}
518
if (strcmp(ev->ev_name, "teken.bg_color") == 0) {
519
/* is it already set? */
520
if (ap->ta_bgcolor == val)
521
return (CMD_OK);
522
a.ta_bgcolor = val;
523
}
524
525
/* Improve visibility */
526
if (a.ta_bgcolor == TC_WHITE)
527
a.ta_bgcolor |= TC_LIGHT;
528
529
teken_set_defattr(&gfx_state.tg_teken, &a);
530
cons_draw_frame(&a);
531
env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
532
teken_input(&gfx_state.tg_teken, "\e[2J", 4);
533
return (CMD_OK);
534
}
535
536
#ifdef TERM_EMU
537
/* Get cursor position. */
538
void
539
get_pos(int *x, int *y)
540
{
541
*x = conout->Mode->CursorColumn;
542
*y = conout->Mode->CursorRow;
543
}
544
545
/* Move cursor to x rows and y cols (0-based). */
546
void
547
curs_move(int *_x, int *_y, int x, int y)
548
{
549
conout->SetCursorPosition(conout, x, y);
550
if (_x != NULL)
551
*_x = conout->Mode->CursorColumn;
552
if (_y != NULL)
553
*_y = conout->Mode->CursorRow;
554
}
555
556
/* Clear internal state of the terminal emulation code. */
557
void
558
end_term(void)
559
{
560
esc = 0;
561
argc = -1;
562
}
563
#endif
564
565
static void
566
efi_cons_rawputchar(int c)
567
{
568
int i;
569
UINTN x, y;
570
conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
571
572
if (c == '\t') {
573
int n;
574
575
n = 8 - ((conout->Mode->CursorColumn + 8) % 8);
576
for (i = 0; i < n; i++)
577
efi_cons_rawputchar(' ');
578
} else {
579
#ifndef TERM_EMU
580
if (c == '\n')
581
efi_cons_efiputchar('\r');
582
efi_cons_efiputchar(c);
583
#else
584
switch (c) {
585
case '\r':
586
curx = 0;
587
efi_cons_efiputchar('\r');
588
return;
589
case '\n':
590
efi_cons_efiputchar('\n');
591
efi_cons_efiputchar('\r');
592
cury++;
593
if (cury >= y)
594
cury--;
595
curx = 0;
596
return;
597
case '\b':
598
if (curx > 0) {
599
efi_cons_efiputchar('\b');
600
curx--;
601
}
602
return;
603
default:
604
efi_cons_efiputchar(c);
605
curx++;
606
if (curx > x-1) {
607
curx = 0;
608
cury++;
609
}
610
if (cury > y-1) {
611
curx = 0;
612
cury--;
613
}
614
}
615
#endif
616
}
617
conout->EnableCursor(conout, TRUE);
618
}
619
620
#ifdef TERM_EMU
621
/* Gracefully exit ESC-sequence processing in case of misunderstanding. */
622
static void
623
bail_out(int c)
624
{
625
char buf[16], *ch;
626
int i;
627
628
if (esc) {
629
efi_cons_rawputchar('\033');
630
if (esc != '\033')
631
efi_cons_rawputchar(esc);
632
for (i = 0; i <= argc; ++i) {
633
sprintf(buf, "%d", args[i]);
634
ch = buf;
635
while (*ch)
636
efi_cons_rawputchar(*ch++);
637
}
638
}
639
efi_cons_rawputchar(c);
640
end_term();
641
}
642
643
/* Clear display from current position to end of screen. */
644
static void
645
CD(void)
646
{
647
int i;
648
UINTN x, y;
649
650
get_pos(&curx, &cury);
651
if (curx == 0 && cury == 0) {
652
conout->ClearScreen(conout);
653
end_term();
654
return;
655
}
656
657
conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
658
CL(0); /* clear current line from cursor to end */
659
for (i = cury + 1; i < y-1; i++) {
660
curs_move(NULL, NULL, 0, i);
661
CL(0);
662
}
663
curs_move(NULL, NULL, curx, cury);
664
end_term();
665
}
666
667
/*
668
* Absolute cursor move to args[0] rows and args[1] columns
669
* (the coordinates are 1-based).
670
*/
671
static void
672
CM(void)
673
{
674
if (args[0] > 0)
675
args[0]--;
676
if (args[1] > 0)
677
args[1]--;
678
curs_move(&curx, &cury, args[1], args[0]);
679
end_term();
680
}
681
682
/* Home cursor (left top corner), also called from mode command. */
683
void
684
HO(void)
685
{
686
argc = 1;
687
args[0] = args[1] = 1;
688
CM();
689
}
690
691
/* Clear line from current position to end of line */
692
static void
693
CL(int direction)
694
{
695
int i, len;
696
UINTN x, y;
697
CHAR16 *line;
698
699
conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
700
switch (direction) {
701
case 0: /* from cursor to end */
702
len = x - curx + 1;
703
break;
704
case 1: /* from beginning to cursor */
705
len = curx;
706
break;
707
case 2: /* entire line */
708
len = x;
709
break;
710
default: /* NOTREACHED */
711
__unreachable();
712
}
713
714
if (cury == y - 1)
715
len--;
716
717
line = malloc(len * sizeof (CHAR16));
718
if (line == NULL) {
719
printf("out of memory\n");
720
return;
721
}
722
for (i = 0; i < len; i++)
723
line[i] = ' ';
724
line[len-1] = 0;
725
726
if (direction != 0)
727
curs_move(NULL, NULL, 0, cury);
728
729
conout->OutputString(conout, line);
730
/* restore cursor position */
731
curs_move(NULL, NULL, curx, cury);
732
free(line);
733
end_term();
734
}
735
736
static void
737
get_arg(int c)
738
{
739
if (argc < 0)
740
argc = 0;
741
args[argc] *= 10;
742
args[argc] += c - '0';
743
}
744
#endif
745
746
/* Emulate basic capabilities of cons25 terminal */
747
static void
748
efi_term_emu(int c)
749
{
750
if (!boot_services_active)
751
return;
752
#ifdef TERM_EMU
753
static int ansi_col[] = {
754
0, 4, 2, 6, 1, 5, 3, 7
755
};
756
int t, i;
757
EFI_STATUS status;
758
759
switch (esc) {
760
case 0:
761
switch (c) {
762
case '\033':
763
esc = c;
764
break;
765
default:
766
efi_cons_rawputchar(c);
767
break;
768
}
769
break;
770
case '\033':
771
switch (c) {
772
case '[':
773
esc = c;
774
args[0] = 0;
775
argc = -1;
776
break;
777
default:
778
bail_out(c);
779
break;
780
}
781
break;
782
case '[':
783
switch (c) {
784
case ';':
785
if (argc < 0)
786
argc = 0;
787
else if (argc + 1 >= MAXARGS)
788
bail_out(c);
789
else
790
args[++argc] = 0;
791
break;
792
case 'H': /* ho = \E[H */
793
if (argc < 0)
794
HO();
795
else if (argc == 1)
796
CM();
797
else
798
bail_out(c);
799
break;
800
case 'J': /* cd = \E[J */
801
if (argc < 0)
802
CD();
803
else
804
bail_out(c);
805
break;
806
case 'm':
807
if (argc < 0) {
808
fg_c = DEFAULT_FGCOLOR;
809
bg_c = DEFAULT_BGCOLOR;
810
}
811
for (i = 0; i <= argc; ++i) {
812
switch (args[i]) {
813
case 0: /* back to normal */
814
fg_c = DEFAULT_FGCOLOR;
815
bg_c = DEFAULT_BGCOLOR;
816
break;
817
case 1: /* bold */
818
fg_c |= 0x8;
819
break;
820
case 4: /* underline */
821
case 5: /* blink */
822
bg_c |= 0x8;
823
break;
824
case 7: /* reverse */
825
t = fg_c;
826
fg_c = bg_c;
827
bg_c = t;
828
break;
829
case 22: /* normal intensity */
830
fg_c &= ~0x8;
831
break;
832
case 24: /* not underline */
833
case 25: /* not blinking */
834
bg_c &= ~0x8;
835
break;
836
case 30: case 31: case 32: case 33:
837
case 34: case 35: case 36: case 37:
838
fg_c = ansi_col[args[i] - 30];
839
break;
840
case 39: /* normal */
841
fg_c = DEFAULT_FGCOLOR;
842
break;
843
case 40: case 41: case 42: case 43:
844
case 44: case 45: case 46: case 47:
845
bg_c = ansi_col[args[i] - 40];
846
break;
847
case 49: /* normal */
848
bg_c = DEFAULT_BGCOLOR;
849
break;
850
}
851
}
852
conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
853
end_term();
854
break;
855
default:
856
if (isdigit(c))
857
get_arg(c);
858
else
859
bail_out(c);
860
break;
861
}
862
break;
863
default:
864
bail_out(c);
865
break;
866
}
867
#else
868
efi_cons_rawputchar(c);
869
#endif
870
}
871
872
static int
873
env_screen_nounset(struct env_var *ev __unused)
874
{
875
if (gfx_state.tg_fb_type == FB_TEXT)
876
return (0);
877
return (EPERM);
878
}
879
880
static void
881
cons_draw_frame(teken_attr_t *a)
882
{
883
teken_attr_t attr = *a;
884
teken_color_t fg = a->ta_fgcolor;
885
886
attr.ta_fgcolor = attr.ta_bgcolor;
887
teken_set_defattr(&gfx_state.tg_teken, &attr);
888
889
gfx_fb_drawrect(0, 0, gfx_state.tg_fb.fb_width,
890
gfx_state.tg_origin.tp_row, 1);
891
gfx_fb_drawrect(0,
892
gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1,
893
gfx_state.tg_fb.fb_width, gfx_state.tg_fb.fb_height, 1);
894
gfx_fb_drawrect(0, gfx_state.tg_origin.tp_row,
895
gfx_state.tg_origin.tp_col,
896
gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, 1);
897
gfx_fb_drawrect(
898
gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col - 1,
899
gfx_state.tg_origin.tp_row, gfx_state.tg_fb.fb_width,
900
gfx_state.tg_fb.fb_height, 1);
901
902
attr.ta_fgcolor = fg;
903
teken_set_defattr(&gfx_state.tg_teken, &attr);
904
}
905
906
bool
907
cons_update_mode(bool use_gfx_mode)
908
{
909
UINTN cols, rows;
910
const teken_attr_t *a;
911
teken_attr_t attr;
912
EFI_STATUS status;
913
char env[10], *ptr;
914
915
if (!efi_started)
916
return (false);
917
918
/*
919
* Despite the use_gfx_mode, we want to make sure we call
920
* efi_find_framebuffer(). This will populate the fb data,
921
* which will be passed to kernel.
922
*/
923
if (efi_find_framebuffer(&gfx_state) == 0 && use_gfx_mode) {
924
int roff, goff, boff;
925
926
roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
927
goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
928
boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
929
930
(void) generate_cons_palette(cmap, COLOR_FORMAT_RGB,
931
gfx_state.tg_fb.fb_mask_red >> roff, roff,
932
gfx_state.tg_fb.fb_mask_green >> goff, goff,
933
gfx_state.tg_fb.fb_mask_blue >> boff, boff);
934
} else {
935
/*
936
* Either text mode was asked by user or we failed to
937
* find frame buffer.
938
*/
939
gfx_state.tg_fb_type = FB_TEXT;
940
}
941
942
status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows);
943
if (EFI_ERROR(status) || cols * rows == 0) {
944
cols = TEXT_COLS;
945
rows = TEXT_ROWS;
946
}
947
948
/*
949
* When we have serial port listed in ConOut, use pre-teken emulator,
950
* if built with.
951
* The problem is, we can not output text on efi and comconsole when
952
* efi also has comconsole bound. But then again, we need to have
953
* terminal emulator for efi text mode to support the menu.
954
* While teken is too expensive to be used on serial console, the
955
* pre-teken emulator is light enough to be used on serial console.
956
*
957
* When doing multiple consoles (both serial and video),
958
* also just use the old emulator. RB_MULTIPLE also implies
959
* we're using a serial console.
960
*/
961
mode = parse_uefi_con_out();
962
if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) {
963
conout->EnableCursor(conout, FALSE);
964
gfx_state.tg_cursor_visible = false;
965
966
if (gfx_state.tg_fb_type == FB_TEXT) {
967
968
gfx_state.tg_functions = &tf;
969
/* ensure the following are not set for text mode */
970
unsetenv("screen.height");
971
unsetenv("screen.width");
972
unsetenv("screen.depth");
973
} else {
974
uint32_t fb_height, fb_width;
975
976
fb_height = gfx_state.tg_fb.fb_height;
977
fb_width = gfx_state.tg_fb.fb_width;
978
979
/*
980
* setup_font() can adjust terminal size.
981
* We can see two kind of bad happening.
982
* We either can get too small console font - requested
983
* terminal size is large, display resolution is
984
* large, and we get very small font.
985
* Or, we can get too large font - requested
986
* terminal size is small and this will cause large
987
* font to be selected.
988
* Now, the setup_font() is updated to consider
989
* display density and this should give us mostly
990
* acceptable font. However, the catch is, not all
991
* display devices will give us display density.
992
* Still, we do hope, external monitors do - this is
993
* where the display size will matter the most.
994
* And for laptop screens, we should still get good
995
* results by requesting 80x25 terminal.
996
*/
997
gfx_state.tg_tp.tp_row = 25;
998
gfx_state.tg_tp.tp_col = 80;
999
setup_font(&gfx_state, fb_height, fb_width);
1000
rows = gfx_state.tg_tp.tp_row;
1001
cols = gfx_state.tg_tp.tp_col;
1002
/* Point of origin in pixels. */
1003
gfx_state.tg_origin.tp_row = (fb_height -
1004
(rows * gfx_state.tg_font.vf_height)) / 2;
1005
gfx_state.tg_origin.tp_col = (fb_width -
1006
(cols * gfx_state.tg_font.vf_width)) / 2;
1007
1008
/* UEFI gop has depth 32. */
1009
gfx_state.tg_glyph_size = gfx_state.tg_font.vf_height *
1010
gfx_state.tg_font.vf_width * 4;
1011
free(gfx_state.tg_glyph);
1012
gfx_state.tg_glyph = malloc(gfx_state.tg_glyph_size);
1013
if (gfx_state.tg_glyph == NULL)
1014
return (false);
1015
1016
gfx_state.tg_functions = &tfx;
1017
snprintf(env, sizeof (env), "%d", fb_height);
1018
env_setenv("screen.height", EV_VOLATILE | EV_NOHOOK,
1019
env, env_noset, env_screen_nounset);
1020
snprintf(env, sizeof (env), "%d", fb_width);
1021
env_setenv("screen.width", EV_VOLATILE | EV_NOHOOK,
1022
env, env_noset, env_screen_nounset);
1023
snprintf(env, sizeof (env), "%d",
1024
gfx_state.tg_fb.fb_bpp);
1025
env_setenv("screen.depth", EV_VOLATILE | EV_NOHOOK,
1026
env, env_noset, env_screen_nounset);
1027
}
1028
1029
/* Record our terminal screen size. */
1030
gfx_state.tg_tp.tp_row = rows;
1031
gfx_state.tg_tp.tp_col = cols;
1032
1033
teken_init(&gfx_state.tg_teken, gfx_state.tg_functions,
1034
&gfx_state);
1035
1036
free(screen_buffer);
1037
screen_buffer = malloc(rows * cols * sizeof(*screen_buffer));
1038
if (screen_buffer != NULL) {
1039
teken_set_winsize(&gfx_state.tg_teken,
1040
&gfx_state.tg_tp);
1041
a = teken_get_defattr(&gfx_state.tg_teken);
1042
attr = *a;
1043
1044
gfx_fb_setcolors(&attr, efi_set_colors, env_nounset);
1045
}
1046
}
1047
1048
if (screen_buffer == NULL) {
1049
conout->EnableCursor(conout, TRUE);
1050
#ifdef TERM_EMU
1051
conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
1052
DEFAULT_BGCOLOR));
1053
end_term();
1054
get_pos(&curx, &cury);
1055
curs_move(&curx, &cury, curx, cury);
1056
fg_c = DEFAULT_FGCOLOR;
1057
bg_c = DEFAULT_BGCOLOR;
1058
#endif
1059
} else {
1060
/* Improve visibility */
1061
if (attr.ta_bgcolor == TC_WHITE)
1062
attr.ta_bgcolor |= TC_LIGHT;
1063
teken_set_defattr(&gfx_state.tg_teken, &attr);
1064
1065
/* Draw frame around terminal area. */
1066
cons_draw_frame(&attr);
1067
/*
1068
* Erase display, this will also fill our screen
1069
* buffer.
1070
*/
1071
teken_input(&gfx_state.tg_teken, "\e[2J", 4);
1072
gfx_state.tg_functions->tf_param(&gfx_state,
1073
TP_SHOWCURSOR, 1);
1074
}
1075
1076
snprintf(env, sizeof (env), "%u", (unsigned)rows);
1077
setenv("LINES", env, 1);
1078
snprintf(env, sizeof (env), "%u", (unsigned)cols);
1079
setenv("COLUMNS", env, 1);
1080
1081
return (true);
1082
}
1083
1084
static int
1085
efi_cons_init(int arg)
1086
{
1087
EFI_STATUS status;
1088
1089
if (efi_started)
1090
return (0);
1091
1092
efi_started = true;
1093
1094
gfx_framework_init();
1095
if (cons_update_mode(gfx_state.tg_fb_type != FB_TEXT))
1096
return (0);
1097
1098
return (1);
1099
}
1100
1101
static void
1102
input_partial(void)
1103
{
1104
unsigned i;
1105
uint32_t c;
1106
1107
if (utf8_left == 0)
1108
return;
1109
1110
for (i = 0; i < sizeof(utf8_partial); i++) {
1111
c = (utf8_partial >> (24 - (i << 3))) & 0xff;
1112
if (c != 0)
1113
efi_term_emu(c);
1114
}
1115
utf8_left = 0;
1116
utf8_partial = 0;
1117
}
1118
1119
static void
1120
input_byte(uint8_t c)
1121
{
1122
if ((c & 0x80) == 0x00) {
1123
/* One-byte sequence. */
1124
input_partial();
1125
efi_term_emu(c);
1126
return;
1127
}
1128
if ((c & 0xe0) == 0xc0) {
1129
/* Two-byte sequence. */
1130
input_partial();
1131
utf8_left = 1;
1132
utf8_partial = c;
1133
return;
1134
}
1135
if ((c & 0xf0) == 0xe0) {
1136
/* Three-byte sequence. */
1137
input_partial();
1138
utf8_left = 2;
1139
utf8_partial = c;
1140
return;
1141
}
1142
if ((c & 0xf8) == 0xf0) {
1143
/* Four-byte sequence. */
1144
input_partial();
1145
utf8_left = 3;
1146
utf8_partial = c;
1147
return;
1148
}
1149
if ((c & 0xc0) == 0x80) {
1150
/* Invalid state? */
1151
if (utf8_left == 0) {
1152
efi_term_emu(c);
1153
return;
1154
}
1155
utf8_left--;
1156
utf8_partial = (utf8_partial << 8) | c;
1157
if (utf8_left == 0) {
1158
uint32_t v, u;
1159
uint8_t b;
1160
1161
v = 0;
1162
u = utf8_partial;
1163
b = (u >> 24) & 0xff;
1164
if (b != 0) { /* Four-byte sequence */
1165
v = b & 0x07;
1166
b = (u >> 16) & 0xff;
1167
v = (v << 6) | (b & 0x3f);
1168
b = (u >> 8) & 0xff;
1169
v = (v << 6) | (b & 0x3f);
1170
b = u & 0xff;
1171
v = (v << 6) | (b & 0x3f);
1172
} else if ((b = (u >> 16) & 0xff) != 0) {
1173
v = b & 0x0f; /* Three-byte sequence */
1174
b = (u >> 8) & 0xff;
1175
v = (v << 6) | (b & 0x3f);
1176
b = u & 0xff;
1177
v = (v << 6) | (b & 0x3f);
1178
} else if ((b = (u >> 8) & 0xff) != 0) {
1179
v = b & 0x1f; /* Two-byte sequence */
1180
b = u & 0xff;
1181
v = (v << 6) | (b & 0x3f);
1182
}
1183
/* Send unicode char directly to console. */
1184
efi_cons_efiputchar(v);
1185
utf8_partial = 0;
1186
}
1187
return;
1188
}
1189
/* Anything left is illegal in UTF-8 sequence. */
1190
input_partial();
1191
efi_term_emu(c);
1192
}
1193
1194
void
1195
efi_cons_putchar(int c)
1196
{
1197
unsigned char ch = c;
1198
1199
/*
1200
* Don't use Teken when we're doing pure serial, or a multiple console
1201
* with video "primary" because that's also serial.
1202
*/
1203
if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || screen_buffer == NULL) {
1204
input_byte(ch);
1205
return;
1206
}
1207
1208
teken_input(&gfx_state.tg_teken, &ch, sizeof (ch));
1209
}
1210
1211
static int
1212
keybuf_getchar(void)
1213
{
1214
int i, c = 0;
1215
1216
for (i = 0; i < KEYBUFSZ; i++) {
1217
if (keybuf[i] != 0) {
1218
c = keybuf[i];
1219
keybuf[i] = 0;
1220
break;
1221
}
1222
}
1223
1224
return (c);
1225
}
1226
1227
static bool
1228
keybuf_ischar(void)
1229
{
1230
int i;
1231
1232
for (i = 0; i < KEYBUFSZ; i++) {
1233
if (keybuf[i] != 0)
1234
return (true);
1235
}
1236
return (false);
1237
}
1238
1239
/*
1240
* We are not reading input before keybuf is empty, so we are safe
1241
* just to fill keybuf from the beginning.
1242
*/
1243
static void
1244
keybuf_inschar(EFI_INPUT_KEY *key)
1245
{
1246
1247
switch (key->ScanCode) {
1248
case SCAN_UP: /* UP */
1249
keybuf[0] = 0x1b; /* esc */
1250
keybuf[1] = '[';
1251
keybuf[2] = 'A';
1252
break;
1253
case SCAN_DOWN: /* DOWN */
1254
keybuf[0] = 0x1b; /* esc */
1255
keybuf[1] = '[';
1256
keybuf[2] = 'B';
1257
break;
1258
case SCAN_RIGHT: /* RIGHT */
1259
keybuf[0] = 0x1b; /* esc */
1260
keybuf[1] = '[';
1261
keybuf[2] = 'C';
1262
break;
1263
case SCAN_LEFT: /* LEFT */
1264
keybuf[0] = 0x1b; /* esc */
1265
keybuf[1] = '[';
1266
keybuf[2] = 'D';
1267
break;
1268
case SCAN_DELETE:
1269
keybuf[0] = CHAR_BACKSPACE;
1270
break;
1271
case SCAN_ESC:
1272
keybuf[0] = 0x1b; /* esc */
1273
break;
1274
default:
1275
keybuf[0] = key->UnicodeChar;
1276
break;
1277
}
1278
}
1279
1280
static bool
1281
efi_readkey(void)
1282
{
1283
EFI_STATUS status;
1284
EFI_INPUT_KEY key;
1285
1286
status = conin->ReadKeyStroke(conin, &key);
1287
if (status == EFI_SUCCESS) {
1288
keybuf_inschar(&key);
1289
return (true);
1290
}
1291
return (false);
1292
}
1293
1294
static bool
1295
efi_readkey_ex(void)
1296
{
1297
EFI_STATUS status;
1298
EFI_INPUT_KEY *kp;
1299
EFI_KEY_DATA key_data;
1300
uint32_t kss;
1301
1302
status = coninex->ReadKeyStrokeEx(coninex, &key_data);
1303
if (status == EFI_SUCCESS) {
1304
kss = key_data.KeyState.KeyShiftState;
1305
kp = &key_data.Key;
1306
if (kss & EFI_SHIFT_STATE_VALID) {
1307
1308
/*
1309
* quick mapping to control chars, replace with
1310
* map lookup later.
1311
*/
1312
if (kss & EFI_RIGHT_CONTROL_PRESSED ||
1313
kss & EFI_LEFT_CONTROL_PRESSED) {
1314
if (kp->UnicodeChar >= 'a' &&
1315
kp->UnicodeChar <= 'z') {
1316
kp->UnicodeChar -= 'a';
1317
kp->UnicodeChar++;
1318
}
1319
}
1320
}
1321
/*
1322
* The shift state and/or toggle state may not be valid,
1323
* but we still can have ScanCode or UnicodeChar.
1324
*/
1325
if (kp->ScanCode == 0 && kp->UnicodeChar == 0)
1326
return (false);
1327
keybuf_inschar(kp);
1328
return (true);
1329
}
1330
return (false);
1331
}
1332
1333
int
1334
efi_cons_getchar(void)
1335
{
1336
int c;
1337
1338
if ((c = keybuf_getchar()) != 0)
1339
return (c);
1340
1341
if (!boot_services_active)
1342
return (-1);
1343
1344
key_pending = 0;
1345
1346
if (coninex == NULL) {
1347
if (efi_readkey())
1348
return (keybuf_getchar());
1349
} else {
1350
if (efi_readkey_ex())
1351
return (keybuf_getchar());
1352
}
1353
1354
return (-1);
1355
}
1356
1357
int
1358
efi_cons_poll(void)
1359
{
1360
EFI_STATUS status;
1361
1362
if (keybuf_ischar() || key_pending)
1363
return (1);
1364
1365
if (!boot_services_active)
1366
return (0);
1367
1368
/*
1369
* Some EFI implementation (u-boot for example) do not support
1370
* WaitForKey().
1371
* CheckEvent() can clear the signaled state.
1372
*/
1373
if (coninex != NULL) {
1374
if (coninex->WaitForKeyEx == NULL) {
1375
key_pending = efi_readkey_ex();
1376
} else {
1377
status = BS->CheckEvent(coninex->WaitForKeyEx);
1378
key_pending = status == EFI_SUCCESS;
1379
}
1380
} else {
1381
if (conin->WaitForKey == NULL) {
1382
key_pending = efi_readkey();
1383
} else {
1384
status = BS->CheckEvent(conin->WaitForKey);
1385
key_pending = status == EFI_SUCCESS;
1386
}
1387
}
1388
1389
return (key_pending);
1390
}
1391
1392
/* Plain direct access to EFI OutputString(). */
1393
void
1394
efi_cons_efiputchar(int c)
1395
{
1396
CHAR16 buf[2];
1397
EFI_STATUS status;
1398
1399
buf[0] = c;
1400
buf[1] = 0; /* terminate string */
1401
1402
status = conout->TestString(conout, buf);
1403
if (EFI_ERROR(status))
1404
buf[0] = '?';
1405
conout->OutputString(conout, buf);
1406
}
1407
1408