1 /*
2 * Copyright (C) 2018-2022 Intel Corporation.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <types.h>
8 #include <pci.h>
9 #include <uart16550.h>
10 #include <shell.h>
11 #include <timer.h>
12 #include <ticks.h>
13 #include <vuart.h>
14 #include <logmsg.h>
15 #include <acrn_hv_defs.h>
16 #include <asm/guest/vm.h>
17 #include <console.h>
18 #include <boot.h>
19 #include <dbg_cmd.h>
20
21 struct hv_timer console_timer;
22
23 #define CONSOLE_KICK_TIMER_TIMEOUT 40UL /* timeout is 40ms*/
24 /* Switching key combinations for shell and uart console */
25 #define GUEST_CONSOLE_ESCAPE_KEY 0x0 /* the "break", put twice to send "break" to guest */
26 #define GUEST_CONSOLE_TO_HV_SWITCH_KEY 'e' /* escape + e to switch back to hv console */
27 uint16_t console_vmid = CONFIG_CONSOLE_DEFAULT_VM;
28
29 /* if use INIT to kick pcpu only, if not notification IPI still is used for sharing CPU */
30 static bool use_init_ipi = false;
31
is_using_init_ipi(void)32 bool is_using_init_ipi(void)
33 {
34 return use_init_ipi;
35 }
36
parse_hvdbg_cmdline(void)37 static void parse_hvdbg_cmdline(void)
38 {
39 const char *start = NULL;
40 const char *end = NULL;
41 struct acrn_boot_info *abi = get_acrn_boot_info();
42
43 start = abi->cmdline;
44
45 while ((*start) != '\0') {
46 while ((*start) == ' ')
47 start++;
48 if ((*start) != '\0') {
49 end = start + 1;
50 while ((*end != ' ') && ((*end) != '\0'))
51 end++;
52
53 if (!handle_dbg_cmd(start, (int32_t)(end - start))) {
54 /* if not handled by handle_dbg_cmd, it can be handled further */
55 if (strncmp(start, "USE_INIT_IPI", (size_t)(end - start)) == 0) {
56 use_init_ipi = true;
57 }
58 }
59 start = end;
60 }
61 }
62
63 }
64
console_init(void)65 void console_init(void)
66 {
67 /*Parse cmdline to get UART setting*/
68 parse_hvdbg_cmdline();
69
70 /*
71 * Enable UART as early as possible.
72 * Then we could use printf for debugging on early boot stage.
73 */
74 uart16550_init(true);
75 }
76
console_putc(const char * ch)77 void console_putc(const char *ch)
78 {
79 (void)uart16550_puts(ch, 1U);
80 }
81
82
console_write(const char * s,size_t len)83 size_t console_write(const char *s, size_t len)
84 {
85 return uart16550_puts(s, len);
86 }
87
console_getc(void)88 char console_getc(void)
89 {
90 return uart16550_getc();
91 }
92
93 /*
94 * @post return != NULL
95 */
vm_console_vuart(struct acrn_vm * vm)96 struct acrn_vuart *vm_console_vuart(struct acrn_vm *vm)
97 {
98 return &vm->vuart[0];
99 }
100
101 /**
102 * @pre vu != NULL
103 * @pre vu->active == true
104 */
vuart_console_rx_chars(struct acrn_vuart * vu)105 static void vuart_console_rx_chars(struct acrn_vuart *vu)
106 {
107 char ch = -1;
108 bool recv = false;
109
110 while (1) {
111 /* Get data from physical uart */
112 ch = uart16550_getc();
113 if (ch == -1)
114 break;
115
116 if (vu->escaping) {
117 vu->escaping = false;
118 switch (ch) {
119 case GUEST_CONSOLE_ESCAPE_KEY:
120 vuart_putchar(vu, ch);
121 vu->lsr |= LSR_BI;
122 recv = true;
123 break;
124 case GUEST_CONSOLE_TO_HV_SWITCH_KEY:
125 /* Switch the console */
126 console_vmid = ACRN_INVALID_VMID;
127 printf("\r\n\r\n ---Entering ACRN SHELL---\r\n");
128 /* following inputs are for hv, don't handle in this loop */
129 goto exit;
130 default:
131 printf("Unknown escaping key: '%c'\r\n", ch);
132 break;
133 }
134 } else {
135 if (ch == GUEST_CONSOLE_ESCAPE_KEY) {
136 vu->escaping = true;
137 } else {
138 vuart_putchar(vu, ch);
139 recv = true;
140 }
141 }
142 }
143
144 exit:
145 if (recv) {
146 vuart_toggle_intr(vu);
147 }
148 }
149
150 /**
151 * @pre vu != NULL
152 */
vuart_console_tx_chars(struct acrn_vuart * vu)153 static void vuart_console_tx_chars(struct acrn_vuart *vu)
154 {
155 char c = vuart_getchar(vu);
156
157 while(c != -1) {
158 printf("%c", c);
159 c = vuart_getchar(vu);
160 }
161 }
162
vuart_console_active(void)163 static struct acrn_vuart *vuart_console_active(void)
164 {
165 struct acrn_vm *vm = NULL;
166 struct acrn_vuart *vu = NULL;
167
168 if (console_vmid < CONFIG_MAX_VM_NUM) {
169 vm = get_vm_from_vmid(console_vmid);
170 if (!is_paused_vm(vm) && !is_poweroff_vm(vm)) {
171 vu = vm_console_vuart(vm);
172 } else {
173 /* Console vm is invalid, switch back to HV-Shell */
174 console_vmid = ACRN_INVALID_VMID;
175 }
176 }
177
178 return ((vu != NULL) && vu->active) ? vu : NULL;
179 }
180
console_timer_callback(__unused void * data)181 static void console_timer_callback(__unused void *data)
182 {
183 struct acrn_vuart *vu;
184
185 /* Kick HV-Shell and Uart-Console tasks */
186 vu = vuart_console_active();
187 if (vu != NULL) {
188 /* serial Console Rx operation */
189 vuart_console_rx_chars(vu);
190 /* serial Console Tx operation */
191 vuart_console_tx_chars(vu);
192 } else {
193 shell_kick();
194 }
195 }
196
console_setup_timer(void)197 void console_setup_timer(void)
198 {
199 uint64_t period_in_cycle, fire_tsc;
200
201 period_in_cycle = TICKS_PER_MS * CONSOLE_KICK_TIMER_TIMEOUT;
202 fire_tsc = cpu_ticks() + period_in_cycle;
203 initialize_timer(&console_timer,
204 console_timer_callback, NULL,
205 fire_tsc, period_in_cycle);
206
207 /* Start an periodic timer */
208 if (add_timer(&console_timer) != 0) {
209 pr_err("Failed to add console kick timer");
210 }
211 }
212
213 /* When lapic-pt is enabled for a vcpu working on the pcpu hosting
214 * console timer, we utilize vm-exits to drive the console.
215 *
216 * Note that currently this approach will result in a laggy shell when
217 * the number of VM-exits/second is low (which is mostly true when lapic-pt is
218 * enabled).
219 */
console_vmexit_callback(struct acrn_vcpu * vcpu)220 void console_vmexit_callback(struct acrn_vcpu *vcpu)
221 {
222 static uint64_t prev_tsc = 0;
223 uint64_t tsc;
224
225 if ((pcpuid_from_vcpu(vcpu) == VUART_TIMER_CPU) && (is_lapic_pt_enabled(vcpu))) {
226 tsc = cpu_ticks();
227 if (tsc - prev_tsc > (TICKS_PER_MS * CONSOLE_KICK_TIMER_TIMEOUT)) {
228 console_timer_callback(NULL);
229 prev_tsc = tsc;
230 }
231 }
232 }
233
suspend_console(void)234 void suspend_console(void)
235 {
236 if (VUART_TIMER_CPU == BSP_CPU_ID) {
237 del_timer(&console_timer);
238 }
239 }
240
resume_console(void)241 void resume_console(void)
242 {
243 if (VUART_TIMER_CPU == BSP_CPU_ID) {
244 console_setup_timer();
245 }
246 }
247