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