1 /*
2  * Copyright (C) 2018-2022 Intel Corporation.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <types.h>
8 #include <errno.h>
9 #include <asm/lib/bits.h>
10 #include "shell_priv.h"
11 #include <asm/irq.h>
12 #include <console.h>
13 #include <asm/per_cpu.h>
14 #include <asm/vmx.h>
15 #include <asm/cpuid.h>
16 #include <asm/ioapic.h>
17 #include <ptdev.h>
18 #include <asm/guest/vm.h>
19 #include <sprintf.h>
20 #include <logmsg.h>
21 #include <version.h>
22 #include <shell.h>
23 #include <asm/guest/vmcs.h>
24 #include <asm/host_pm.h>
25 
26 #define TEMP_STR_SIZE		60U
27 #define MAX_STR_SIZE		256U
28 #define SHELL_PROMPT_STR	"ACRN:\\>"
29 
30 #define SHELL_LOG_BUF_SIZE		(PAGE_SIZE * MAX_PCPU_NUM / 2U)
31 static char shell_log_buf[SHELL_LOG_BUF_SIZE];
32 
33 /* Input Line Other - Switch to the "other" input line (there are only two
34  * input lines total).
35  */
36 
37 static int32_t shell_cmd_help(__unused int32_t argc, __unused char **argv);
38 static int32_t shell_version(__unused int32_t argc, __unused char **argv);
39 static int32_t shell_list_vm(__unused int32_t argc, __unused char **argv);
40 static int32_t shell_list_vcpu(__unused int32_t argc, __unused char **argv);
41 static int32_t shell_vcpu_dumpreg(int32_t argc, char **argv);
42 static int32_t shell_dump_host_mem(int32_t argc, char **argv);
43 static int32_t shell_dump_guest_mem(int32_t argc, char **argv);
44 static int32_t shell_to_vm_console(int32_t argc, char **argv);
45 static int32_t shell_show_cpu_int(__unused int32_t argc, __unused char **argv);
46 static int32_t shell_show_ptdev_info(__unused int32_t argc, __unused char **argv);
47 static int32_t shell_show_vioapic_info(int32_t argc, char **argv);
48 static int32_t shell_show_ioapic_info(__unused int32_t argc, __unused char **argv);
49 static int32_t shell_loglevel(int32_t argc, char **argv);
50 static int32_t shell_cpuid(int32_t argc, char **argv);
51 static int32_t shell_reboot(int32_t argc, char **argv);
52 static int32_t shell_rdmsr(int32_t argc, char **argv);
53 static int32_t shell_wrmsr(int32_t argc, char **argv);
54 
55 static struct shell_cmd shell_cmds[] = {
56 	{
57 		.str		= SHELL_CMD_HELP,
58 		.cmd_param	= SHELL_CMD_HELP_PARAM,
59 		.help_str	= SHELL_CMD_HELP_HELP,
60 		.fcn		= shell_cmd_help,
61 	},
62 	{
63 		.str		= SHELL_CMD_VERSION,
64 		.cmd_param	= SHELL_CMD_VERSION_PARAM,
65 		.help_str	= SHELL_CMD_VERSION_HELP,
66 		.fcn		= shell_version,
67 	},
68 	{
69 		.str		= SHELL_CMD_VM_LIST,
70 		.cmd_param	= SHELL_CMD_VM_LIST_PARAM,
71 		.help_str	= SHELL_CMD_VM_LIST_HELP,
72 		.fcn		= shell_list_vm,
73 	},
74 	{
75 		.str		= SHELL_CMD_VCPU_LIST,
76 		.cmd_param	= SHELL_CMD_VCPU_LIST_PARAM,
77 		.help_str	= SHELL_CMD_VCPU_LIST_HELP,
78 		.fcn		= shell_list_vcpu,
79 	},
80 	{
81 		.str		= SHELL_CMD_VCPU_DUMPREG,
82 		.cmd_param	= SHELL_CMD_VCPU_DUMPREG_PARAM,
83 		.help_str	= SHELL_CMD_VCPU_DUMPREG_HELP,
84 		.fcn		= shell_vcpu_dumpreg,
85 	},
86 	{
87 		.str		= SHELL_CMD_DUMP_HOST_MEM,
88 		.cmd_param	= SHELL_CMD_DUMP_HOST_MEM_PARAM,
89 		.help_str	= SHELL_CMD_DUMP_HOST_MEM_HELP,
90 		.fcn		= shell_dump_host_mem,
91 	},
92 	{
93 		.str		= SHELL_CMD_DUMP_GUEST_MEM,
94 		.cmd_param	= SHELL_CMD_DUMP_GUEST_MEM_PARAM,
95 		.help_str	= SHELL_CMD_DUMP_GUEST_MEM_HELP,
96 		.fcn		= shell_dump_guest_mem,
97 	},
98 	{
99 		.str		= SHELL_CMD_VM_CONSOLE,
100 		.cmd_param	= SHELL_CMD_VM_CONSOLE_PARAM,
101 		.help_str	= SHELL_CMD_VM_CONSOLE_HELP,
102 		.fcn		= shell_to_vm_console,
103 	},
104 	{
105 		.str		= SHELL_CMD_INTERRUPT,
106 		.cmd_param	= SHELL_CMD_INTERRUPT_PARAM,
107 		.help_str	= SHELL_CMD_INTERRUPT_HELP,
108 		.fcn		= shell_show_cpu_int,
109 	},
110 	{
111 		.str		= SHELL_CMD_PTDEV,
112 		.cmd_param	= SHELL_CMD_PTDEV_PARAM,
113 		.help_str	= SHELL_CMD_PTDEV_HELP,
114 		.fcn		= shell_show_ptdev_info,
115 	},
116 	{
117 		.str		= SHELL_CMD_VIOAPIC,
118 		.cmd_param	= SHELL_CMD_VIOAPIC_PARAM,
119 		.help_str	= SHELL_CMD_VIOAPIC_HELP,
120 		.fcn		= shell_show_vioapic_info,
121 	},
122 	{
123 		.str		= SHELL_CMD_IOAPIC,
124 		.cmd_param	= SHELL_CMD_IOAPIC_PARAM,
125 		.help_str	= SHELL_CMD_IOAPIC_HELP,
126 		.fcn		= shell_show_ioapic_info,
127 	},
128 	{
129 		.str		= SHELL_CMD_LOG_LVL,
130 		.cmd_param	= SHELL_CMD_LOG_LVL_PARAM,
131 		.help_str	= SHELL_CMD_LOG_LVL_HELP,
132 		.fcn		= shell_loglevel,
133 	},
134 	{
135 		.str		= SHELL_CMD_CPUID,
136 		.cmd_param	= SHELL_CMD_CPUID_PARAM,
137 		.help_str	= SHELL_CMD_CPUID_HELP,
138 		.fcn		= shell_cpuid,
139 	},
140 	{
141 		.str		= SHELL_CMD_REBOOT,
142 		.cmd_param	= SHELL_CMD_REBOOT_PARAM,
143 		.help_str	= SHELL_CMD_REBOOT_HELP,
144 		.fcn		= shell_reboot,
145 	},
146 	{
147 		.str		= SHELL_CMD_RDMSR,
148 		.cmd_param	= SHELL_CMD_RDMSR_PARAM,
149 		.help_str	= SHELL_CMD_RDMSR_HELP,
150 		.fcn		= shell_rdmsr,
151 	},
152 	{
153 		.str		= SHELL_CMD_WRMSR,
154 		.cmd_param	= SHELL_CMD_WRMSR_PARAM,
155 		.help_str	= SHELL_CMD_WRMSR_HELP,
156 		.fcn		= shell_wrmsr,
157 	},
158 };
159 
160 /* for function key: up/down/right/left/home/end and delete key */
161 enum function_key {
162 	KEY_NONE,
163 
164 	KEY_DELETE = 0x5B33,
165 	KEY_UP = 0x5B41,
166 	KEY_DOWN = 0x5B42,
167 	KEY_RIGHT = 0x5B43,
168 	KEY_LEFT = 0x5B44,
169 	KEY_END = 0x5B46,
170 	KEY_HOME = 0x5B48,
171 };
172 
173 /* The initial log level*/
174 uint16_t console_loglevel = CONFIG_CONSOLE_LOGLEVEL_DEFAULT;
175 uint16_t mem_loglevel = CONFIG_MEM_LOGLEVEL_DEFAULT;
176 uint16_t npk_loglevel = CONFIG_NPK_LOGLEVEL_DEFAULT;
177 
178 static struct shell hv_shell;
179 static struct shell *p_shell = &hv_shell;
180 
string_to_argv(char * argv_str,void * p_argv_mem,__unused uint32_t argv_mem_size,uint32_t * p_argc,char *** p_argv)181 static int32_t string_to_argv(char *argv_str, void *p_argv_mem,
182 		__unused uint32_t argv_mem_size,
183 		uint32_t *p_argc, char ***p_argv)
184 {
185 	uint32_t argc;
186 	char **argv;
187 	char *p_ch;
188 
189 	/* Setup initial argument values. */
190 	argc = 0U;
191 	argv = NULL;
192 
193 	/* Ensure there are arguments to be processed. */
194 	if (argv_str == NULL) {
195 		*p_argc = argc;
196 		*p_argv = argv;
197 		return -EINVAL;
198 	}
199 
200 	/* Process the argument string (there is at least one element). */
201 	argv = (char **)p_argv_mem;
202 	p_ch = argv_str;
203 
204 	/* Remove all spaces at the beginning of cmd*/
205 	while (*p_ch == ' ') {
206 		p_ch++;
207 	}
208 
209 	while (*p_ch != 0) {
210 		/* Add argument (string) pointer to the vector. */
211 		argv[argc] = p_ch;
212 
213 		/* Move past the vector entry argument string (in the
214 		 * argument string).
215 		 */
216 		while ((*p_ch != ' ') && (*p_ch != ',') && (*p_ch != 0)) {
217 			p_ch++;
218 		}
219 
220 		/* Count the argument just processed. */
221 		argc++;
222 
223 		/* Check for the end of the argument string. */
224 		if (*p_ch != 0) {
225 			/* Terminate the vector entry argument string
226 			 * and move to the next.
227 			 */
228 			*p_ch = 0;
229 			/* Remove all space in middile of cmdline */
230 			p_ch++;
231 			while (*p_ch == ' ') {
232 				p_ch++;
233 			}
234 		}
235 	}
236 
237 	/* Update return parameters */
238 	*p_argc = argc;
239 	*p_argv = argv;
240 
241 	return 0;
242 }
243 
shell_find_cmd(const char * cmd_str)244 static struct shell_cmd *shell_find_cmd(const char *cmd_str)
245 {
246 	uint32_t i;
247 	struct shell_cmd *p_cmd = NULL;
248 
249 	for (i = 0U; i < p_shell->cmd_count; i++) {
250 		p_cmd = &p_shell->cmds[i];
251 		if (strcmp(p_cmd->str, cmd_str) == 0) {
252 			return p_cmd;
253 		}
254 	}
255 	return NULL;
256 }
257 
shell_getc(void)258 static char shell_getc(void)
259 {
260 	return console_getc();
261 }
262 
shell_puts(const char * string_ptr)263 static void shell_puts(const char *string_ptr)
264 {
265 	/* Output the string */
266 	(void)console_write(string_ptr, strnlen_s(string_ptr,
267 				SHELL_STRING_MAX_LEN));
268 }
269 
sanitize_vmid(uint16_t vmid)270 static uint16_t sanitize_vmid(uint16_t vmid)
271 {
272 	uint16_t sanitized_vmid = vmid;
273 	char temp_str[TEMP_STR_SIZE];
274 
275 	if (vmid >= CONFIG_MAX_VM_NUM) {
276 		snprintf(temp_str, TEMP_STR_SIZE,
277 			"VM ID given exceeds the MAX_VM_NUM(%u), using 0 instead\r\n",
278 			CONFIG_MAX_VM_NUM);
279 		shell_puts(temp_str);
280 		sanitized_vmid = 0U;
281 	}
282 
283 	return sanitized_vmid;
284 }
285 
clear_input_line(uint32_t len)286 static void clear_input_line(uint32_t len)
287 {
288 	while (len > 0) {
289 		len--;
290 		shell_puts("\b");
291 		shell_puts(" \b");
292 	}
293 }
294 
set_cursor_pos(uint32_t left_offset)295 static void set_cursor_pos(uint32_t left_offset)
296 {
297 	while (left_offset > 0) {
298 		left_offset--;
299 		shell_puts("\b");
300 	}
301 }
302 
handle_delete_key(void)303 static void handle_delete_key(void)
304 {
305 	if (p_shell->cursor_offset < p_shell->input_line_len) {
306 
307 		uint32_t delta = p_shell->input_line_len - p_shell->cursor_offset - 1;
308 
309 		/* Send a space + backspace sequence to delete character */
310 		shell_puts(" \b");
311 
312 		/* display the left input chars and remove former last one */
313 		shell_puts(p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset + 1);
314 		shell_puts(" \b");
315 
316 		set_cursor_pos(delta);
317 
318 		memcpy_erms(p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset,
319 			p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset + 1, delta);
320 
321 		/* Null terminate the last character to erase it */
322 		p_shell->buffered_line[p_shell->input_line_active][p_shell->input_line_len - 1] = 0;
323 
324 		/* Reduce the length of the string by one */
325 		p_shell->input_line_len--;
326 	}
327 }
328 
handle_updown_key(enum function_key key_value)329 static void handle_updown_key(enum function_key key_value)
330 {
331 	int32_t to_select, current_select = p_shell->to_select_index;
332 
333 	/* update current_select and p_shell->to_select_index as up/down key */
334 	if (key_value == KEY_UP) {
335 		/* if the ring buffer not full, just decrease one until to 0; if full, need handle overflow case */
336 		to_select = p_shell->to_select_index - 1;
337 		if (to_select < 0) {
338 			to_select += MAX_BUFFERED_CMDS;
339 		}
340 
341 		if (p_shell->buffered_line[to_select][0] != '\0') {
342 			current_select = to_select;
343 		}
344 
345 	} else {
346 		/* if down key and current is active line, not need update */
347 		if (p_shell->to_select_index != p_shell->input_line_active) {
348 			current_select = (p_shell->to_select_index + 1) % MAX_BUFFERED_CMDS;
349 		}
350 	}
351 
352 	/* go up/down until first buffered cmd or current input line: user will know it is end to select */
353 	if (current_select != p_shell->input_line_active) {
354 		p_shell->to_select_index = current_select;
355 	}
356 
357 	if (strcmp(p_shell->buffered_line[current_select], p_shell->buffered_line[p_shell->input_line_active]) != 0) {
358 		/* reset cursor pos and clear current input line first, then output selected cmd */
359 		if (p_shell->cursor_offset < p_shell->input_line_len) {
360 			shell_puts(p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset);
361 		}
362 
363 		clear_input_line(p_shell->input_line_len);
364 		shell_puts(p_shell->buffered_line[current_select]);
365 
366 		size_t len = strnlen_s(p_shell->buffered_line[current_select], SHELL_CMD_MAX_LEN);
367 
368 		memcpy_s(p_shell->buffered_line[p_shell->input_line_active], SHELL_CMD_MAX_LEN,
369 			p_shell->buffered_line[current_select], len + 1);
370 		p_shell->input_line_len = len;
371 		p_shell->cursor_offset = len;
372 	}
373 }
374 
shell_handle_special_char(char ch)375 static void shell_handle_special_char(char ch)
376 {
377 	enum function_key key_value = KEY_NONE;
378 
379 	switch (ch) {
380 	/* original function key value: ESC + key (2/3 bytes), so consume the next 2/3 characters */
381 	case 0x1b:
382 		key_value = (shell_getc() << 8) | shell_getc();
383 		if (key_value == KEY_DELETE) {
384 			(void)shell_getc(); /* delete key has one more byte */
385 		}
386 
387 		switch (key_value) {
388 		case KEY_DELETE:
389 			handle_delete_key();
390 			break;
391 		case KEY_UP:
392 		case KEY_DOWN:
393 			handle_updown_key(key_value);
394 			break;
395 		case KEY_RIGHT:
396 			if (p_shell->cursor_offset < p_shell->input_line_len) {
397 				shell_puts(p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset);
398 				p_shell->cursor_offset++;
399 				set_cursor_pos(p_shell->input_line_len - p_shell->cursor_offset);
400 			}
401 			break;
402 		case KEY_LEFT:
403 			if (p_shell->cursor_offset > 0) {
404 				p_shell->cursor_offset--;
405 				shell_puts("\b");
406 			}
407 			break;
408 		case KEY_END:
409 			if (p_shell->cursor_offset < p_shell->input_line_len) {
410 				shell_puts(p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset);
411 				p_shell->cursor_offset = p_shell->input_line_len;
412 			}
413 			break;
414 		case KEY_HOME:
415 			if (p_shell->cursor_offset > 0) {
416 				set_cursor_pos(p_shell->cursor_offset);
417 				p_shell->cursor_offset = 0;
418 			}
419 			break;
420 		default:
421 			break;
422 		}
423 
424 		break;
425 	default:
426 		/*
427 		 * Only the Escape character is treated as special character.
428 		 * All the other characters have been handled properly in
429 		 * shell_input_line, so they will not be handled in this API.
430 		 * Gracefully return if prior case clauses have not been met.
431 		 */
432 		break;
433 	}
434 }
435 
handle_backspace_key(void)436 static void handle_backspace_key(void)
437 {
438 	/* Ensure length is not 0 */
439 	if (p_shell->cursor_offset > 0U) {
440 		/* Echo backspace */
441 		shell_puts("\b");
442 		/* Send a space + backspace sequence to delete character */
443 		shell_puts(" \b");
444 
445 		if (p_shell->cursor_offset < p_shell->input_line_len) {
446 			uint32_t delta = p_shell->input_line_len - p_shell->cursor_offset;
447 
448 			/* display the left input-chars and remove the former last one */
449 			shell_puts(p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset);
450 			shell_puts(" \b");
451 
452 			set_cursor_pos(delta);
453 			memcpy_erms(p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset - 1,
454 				p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset, delta);
455 		}
456 
457 		/* Null terminate the last character to erase it */
458 		p_shell->buffered_line[p_shell->input_line_active][p_shell->input_line_len - 1] = 0;
459 
460 		/* Reduce the length of the string by one */
461 		p_shell->input_line_len--;
462 		p_shell->cursor_offset--;
463 	}
464 }
465 
handle_input_char(char ch)466 static void handle_input_char(char ch)
467 {
468 	uint32_t delta = p_shell->input_line_len - p_shell->cursor_offset;
469 
470 	/* move the input from cursor offset back first */
471 	if (delta > 0) {
472 		memcpy_erms_backwards(p_shell->buffered_line[p_shell->input_line_active] + p_shell->input_line_len,
473 			p_shell->buffered_line[p_shell->input_line_active] + p_shell->input_line_len - 1, delta);
474 	}
475 
476 	p_shell->buffered_line[p_shell->input_line_active][p_shell->cursor_offset] = ch;
477 
478 	/* Echo back the input */
479 	shell_puts(p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset);
480 	set_cursor_pos(delta);
481 
482 	/* Move to next character in string */
483 	p_shell->input_line_len++;
484 	p_shell->cursor_offset++;
485 }
486 
shell_input_line(void)487 static bool shell_input_line(void)
488 {
489 	bool done = false;
490 	char ch;
491 
492 	ch = shell_getc();
493 
494 	/* Check character */
495 	switch (ch) {
496 	/* Backspace */
497 	case '\b':
498 		handle_backspace_key();
499 		break;
500 
501 	/* Carriage-return */
502 	case '\r':
503 		/* Echo carriage return / line feed */
504 		shell_puts("\r\n");
505 
506 		/* Set flag showing line input done */
507 		done = true;
508 
509 		/* Reset command length for next command processing */
510 		p_shell->input_line_len = 0U;
511 		p_shell->cursor_offset = 0U;
512 		break;
513 
514 	/* Line feed */
515 	case '\n':
516 		/* Do nothing */
517 		break;
518 
519 	/* All other characters */
520 	default:
521 		/* Ensure data doesn't exceed full terminal width */
522 		if (p_shell->input_line_len < SHELL_CMD_MAX_LEN) {
523 			/* See if a "standard" prINTable ASCII character received */
524 			if ((ch >= 32) && (ch <= 126)) {
525 				handle_input_char(ch);
526 			} else {
527 				/* call special character handler */
528 				shell_handle_special_char(ch);
529 			}
530 		} else {
531 			/* Echo carriage return / line feed */
532 			shell_puts("\r\n");
533 
534 			/* Set flag showing line input done */
535 			done = true;
536 
537 			/* Reset command length for next command processing */
538 			p_shell->input_line_len = 0U;
539 			p_shell->cursor_offset = 0U;
540 		}
541 		break;
542 	}
543 
544 
545 	return done;
546 }
547 
shell_process_cmd(const char * p_input_line)548 static int32_t shell_process_cmd(const char *p_input_line)
549 {
550 	int32_t status = -EINVAL;
551 	struct shell_cmd *p_cmd;
552 	char cmd_argv_str[SHELL_CMD_MAX_LEN + 1U];
553 	int32_t cmd_argv_mem[sizeof(char *) * ((SHELL_CMD_MAX_LEN + 1U) >> 1U)];
554 	int32_t cmd_argc;
555 	char **cmd_argv;
556 
557 	/* Copy the input line INTo an argument string to become part of the
558 	 * argument vector.
559 	 */
560 	(void)strncpy_s(&cmd_argv_str[0], SHELL_CMD_MAX_LEN + 1U, p_input_line, SHELL_CMD_MAX_LEN);
561 	cmd_argv_str[SHELL_CMD_MAX_LEN] = 0;
562 
563 	/* Build the argv vector from the string. The first argument in the
564 	 * resulting vector will be the command string itself.
565 	 */
566 
567 	/* NOTE: This process is destructive to the argument string! */
568 
569 	(void) string_to_argv(&cmd_argv_str[0],
570 			(void *) &cmd_argv_mem[0],
571 			sizeof(cmd_argv_mem), (void *)&cmd_argc, &cmd_argv);
572 
573 	/* Determine if there is a command to process. */
574 	if (cmd_argc != 0) {
575 		/* See if command is in cmds supported */
576 		p_cmd = shell_find_cmd(cmd_argv[0]);
577 		if (p_cmd == NULL) {
578 			shell_puts("\r\nError: Invalid command.\r\n");
579 			return -EINVAL;
580 		}
581 
582 		status = p_cmd->fcn(cmd_argc, &cmd_argv[0]);
583 		if (status == -EINVAL) {
584 			shell_puts("\r\nError: Invalid parameters.\r\n");
585 		} else if (status != 0) {
586 			shell_puts("\r\nCommand launch failed.\r\n");
587 		} else {
588 			/* No other state currently, do nothing */
589 		}
590 	}
591 
592 	return status;
593 }
594 
shell_process(void)595 static int32_t shell_process(void)
596 {
597 	int32_t status, former_index;
598 	char *p_input_line;
599 
600 	/* Process current command (using active input line). */
601 	p_input_line = p_shell->buffered_line[p_shell->input_line_active];
602 
603 	former_index = (p_shell->input_line_active + MAX_BUFFERED_CMDS - 1) % MAX_BUFFERED_CMDS;
604 
605 	/* just buffer current cmd if current is not empty and not same with last buffered one */
606 	if ((strnlen_s(p_input_line, SHELL_CMD_MAX_LEN) > 0) &&
607 		(strcmp(p_input_line, p_shell->buffered_line[former_index]) != 0)) {
608 		p_shell->input_line_active = (p_shell->input_line_active + 1) % MAX_BUFFERED_CMDS;
609 	}
610 
611 	p_shell->to_select_index = p_shell->input_line_active;
612 
613 	/* Process command */
614 	status = shell_process_cmd(p_input_line);
615 
616 	/* Now that the command is processed, zero fill the input buffer */
617 	(void)memset(p_shell->buffered_line[p_shell->input_line_active], 0, SHELL_CMD_MAX_LEN + 1U);
618 
619 	/* Process command and return result to caller */
620 	return status;
621 }
622 
623 
shell_kick(void)624 void shell_kick(void)
625 {
626 	static bool is_cmd_cmplt = true;
627 
628 	/* At any given instance, UART may be owned by the HV
629 	 * OR by the guest that has enabled the vUart.
630 	 * Show HV shell prompt ONLY when HV owns the
631 	 * serial port.
632 	 */
633 	/* Prompt the user for a selection. */
634 	if (is_cmd_cmplt) {
635 		shell_puts(SHELL_PROMPT_STR);
636 	}
637 
638 	/* Get user's input */
639 	is_cmd_cmplt = shell_input_line();
640 
641 	/* If user has pressed the ENTER then process
642 	 * the command
643 	 */
644 	if (is_cmd_cmplt) {
645 		/* Process current input line. */
646 		(void)shell_process();
647 	}
648 }
649 
650 
shell_init(void)651 void shell_init(void)
652 {
653 	p_shell->cmds = shell_cmds;
654 	p_shell->cmd_count = ARRAY_SIZE(shell_cmds);
655 
656 	p_shell->to_select_index = 0;
657 
658 	/* Zero fill the input buffer */
659 	(void)memset(p_shell->buffered_line[p_shell->input_line_active], 0U, SHELL_CMD_MAX_LEN + 1U);
660 }
661 
662 #define SHELL_ROWS	30
663 #define MAX_OUTPUT_LEN  80
shell_cmd_help(__unused int32_t argc,__unused char ** argv)664 static int32_t shell_cmd_help(__unused int32_t argc, __unused char **argv)
665 {
666 	struct shell_cmd *p_cmd = NULL;
667 
668 	char str[MAX_STR_SIZE];
669 	char* help_str;
670 	/* Print title */
671 	shell_puts("\r\nRegistered Commands:\r\n\r\n");
672 
673 	pr_dbg("shell: Number of registered commands = %u in %s\n",
674 		p_shell->cmd_count, __func__);
675 
676 	/* Proceed based on the number of registered commands. */
677 	if (p_shell->cmd_count == 0U) {
678 		/* No registered commands */
679 		shell_puts("NONE\r\n");
680 	} else {
681 		int32_t i = 0;
682 		uint32_t j;
683 
684 		for (j = 0U; j < p_shell->cmd_count; j++) {
685 			p_cmd = &p_shell->cmds[j];
686 
687 			/* Check if we've filled the screen with info */
688 			/* i + 1 used to avoid 0%SHELL_ROWS=0 */
689 			if (((i + 1) % SHELL_ROWS) == 0) {
690 				/* Pause before we continue on to the next
691 				 * page.
692 				 */
693 
694 				/* Print message to the user. */
695 				shell_puts("<*** Hit any key to continue ***>");
696 
697 				/* Wait for a character from user (NOT USED) */
698 				(void)shell_getc();
699 
700 				/* Print a new line after the key is hit. */
701 				shell_puts("\r\n");
702 			}
703 
704 			i++;
705 			if (p_cmd->cmd_param == NULL)
706 				p_cmd->cmd_param = " ";
707 			(void)memset(str, ' ', sizeof(str));
708 			/* Output the command & parameter string */
709 			snprintf(str, MAX_OUTPUT_LEN, " %-15s%-64s",
710 					p_cmd->str, p_cmd->cmd_param);
711 			shell_puts(str);
712 			shell_puts("\r\n");
713 
714 			help_str = p_cmd->help_str;
715 			while (strnlen_s(help_str, MAX_OUTPUT_LEN > 0)) {
716 				(void)memset(str, ' ', sizeof(str));
717 				if (strnlen_s(help_str, MAX_OUTPUT_LEN) > 65) {
718 					snprintf(str, MAX_OUTPUT_LEN, "               %-s", help_str);
719 					shell_puts(str);
720 					shell_puts("\r\n");
721 					help_str = help_str + 65;
722 				} else {
723 					snprintf(str, MAX_OUTPUT_LEN, "               %-s", help_str);
724 					shell_puts(str);
725 					shell_puts("\r\n");
726 					break;
727 				}
728 			}
729 		}
730 	}
731 
732 	shell_puts("\r\n");
733 
734 	return 0;
735 }
736 
shell_version(__unused int32_t argc,__unused char ** argv)737 static int32_t shell_version(__unused int32_t argc, __unused char **argv)
738 {
739 	char temp_str[MAX_STR_SIZE];
740 
741 	snprintf(temp_str, MAX_STR_SIZE, "HV: %s-%s-%s %s%s%s%s %s@%s build by %s %s\r\n",
742 		HV_BRANCH_VERSION, HV_COMMIT_TIME, HV_COMMIT_DIRTY, HV_BUILD_TYPE,
743 		(sizeof(HV_COMMIT_TAGS) > 1) ? "(tag: " : "", HV_COMMIT_TAGS,
744 		(sizeof(HV_COMMIT_TAGS) > 1) ? ")" : "",
745 		HV_BUILD_SCENARIO, HV_BUILD_BOARD, HV_BUILD_USER, HV_BUILD_TIME);
746 	shell_puts(temp_str);
747 
748 	return 0;
749 }
750 
shell_list_vm(__unused int32_t argc,__unused char ** argv)751 static int32_t shell_list_vm(__unused int32_t argc, __unused char **argv)
752 {
753 	char temp_str[MAX_STR_SIZE];
754 	struct acrn_vm *vm;
755 	struct acrn_vm_config *vm_config;
756 	uint16_t vm_id;
757 	char state[32];
758 
759 	shell_puts("\r\nVM_ID VM_NAME                          VM_STATE"
760 		   "\r\n===== ================================ ========\r\n");
761 
762 	for (vm_id = 0U; vm_id < CONFIG_MAX_VM_NUM; vm_id++) {
763 		vm = get_vm_from_vmid(vm_id);
764 		switch (vm->state) {
765 		case VM_CREATED:
766 			(void)strncpy_s(state, 32U, "Created", 32U);
767 			break;
768 		case VM_RUNNING:
769 			(void)strncpy_s(state, 32U, "Running", 32U);
770 			break;
771 		case VM_PAUSED:
772 			(void)strncpy_s(state, 32U, "Paused", 32U);
773 			break;
774 		case VM_POWERED_OFF:
775 			(void)strncpy_s(state, 32U, "Off", 32U);
776 			break;
777 		default:
778 			(void)strncpy_s(state, 32U, "Unknown", 32U);
779 			break;
780 		}
781 		vm_config = get_vm_config(vm_id);
782 		if (!is_poweroff_vm(vm)) {
783 			snprintf(temp_str, MAX_STR_SIZE, "  %-3d %-32s %-8s\r\n",
784 				vm_id, vm_config->name, state);
785 
786 			/* Output information for this task */
787 			shell_puts(temp_str);
788 		}
789 	}
790 
791 	return 0;
792 }
793 
shell_list_vcpu(__unused int32_t argc,__unused char ** argv)794 static int32_t shell_list_vcpu(__unused int32_t argc, __unused char **argv)
795 {
796 	char temp_str[MAX_STR_SIZE];
797 	struct acrn_vm *vm;
798 	struct acrn_vcpu *vcpu;
799 	char vcpu_state_str[32], thread_state_str[32];
800 	uint16_t i;
801 	uint16_t idx;
802 
803 	shell_puts("\r\nVM ID    PCPU ID    VCPU ID    VCPU ROLE    VCPU STATE    THREAD STATE"
804 		"\r\n=====    =======    =======    =========    ==========    ==========\r\n");
805 
806 	for (idx = 0U; idx < CONFIG_MAX_VM_NUM; idx++) {
807 		vm = get_vm_from_vmid(idx);
808 		if (is_poweroff_vm(vm)) {
809 			continue;
810 		}
811 		foreach_vcpu(i, vm, vcpu) {
812 			switch (vcpu->state) {
813 			case VCPU_INIT:
814 				(void)strncpy_s(vcpu_state_str, 32U, "Init", 32U);
815 				break;
816 			case VCPU_RUNNING:
817 				(void)strncpy_s(vcpu_state_str, 32U, "Running", 32U);
818 				break;
819 			case VCPU_ZOMBIE:
820 				(void)strncpy_s(vcpu_state_str, 32U, "Zombie", 32U);
821 				break;
822 			default:
823 				(void)strncpy_s(vcpu_state_str, 32U, "Unknown", 32U);
824 				break;
825 			}
826 
827 			switch (vcpu->thread_obj.status) {
828 			case THREAD_STS_RUNNING:
829 				(void)strncpy_s(thread_state_str, 32U, "RUNNING", 32U);
830 				break;
831 			case THREAD_STS_RUNNABLE:
832 				(void)strncpy_s(thread_state_str, 32U, "RUNNABLE", 32U);
833 				break;
834 			case THREAD_STS_BLOCKED:
835 				(void)strncpy_s(thread_state_str, 32U, "BLOCKED", 32U);
836 				break;
837 			default:
838 				(void)strncpy_s(thread_state_str, 32U, "UNKNOWN", 32U);
839 				break;
840 			}
841 			/* Create output string consisting of VM name
842 			 * and VM id
843 			 */
844 			snprintf(temp_str, MAX_STR_SIZE,
845 					"  %-9d %-10d %-7hu %-12s %-16s %-16s\r\n",
846 					vm->vm_id,
847 					pcpuid_from_vcpu(vcpu),
848 					vcpu->vcpu_id,
849 					is_vcpu_bsp(vcpu) ?
850 					"PRIMARY" : "SECONDARY",
851 					vcpu_state_str, thread_state_str);
852 			/* Output information for this task */
853 			shell_puts(temp_str);
854 		}
855 	}
856 
857 	return 0;
858 }
859 
860 #define DUMPREG_SP_SIZE	32
861 /* the input 'data' must != NULL and indicate a vcpu structure pointer */
dump_vcpu_reg(void * data)862 static void dump_vcpu_reg(void *data)
863 {
864 	int32_t status;
865 	uint64_t i, fault_addr, tmp[DUMPREG_SP_SIZE];
866 	uint32_t err_code = 0;
867 	struct vcpu_dump *dump = data;
868 	struct acrn_vcpu *vcpu = dump->vcpu;
869 	char *str = dump->str;
870 	size_t len, size = dump->str_max;
871 	uint16_t pcpu_id = get_pcpu_id();
872 	struct acrn_vcpu *curr = get_running_vcpu(pcpu_id);
873 
874 	/* switch vmcs */
875 	load_vmcs(vcpu);
876 
877 	len = snprintf(str, size,
878 		"=  VM ID %d ==== CPU ID %hu========================\r\n"
879 		"=  RIP=0x%016lx  RSP=0x%016lx RFLAGS=0x%016lx\r\n"
880 		"=  CR0=0x%016lx  CR2=0x%016lx\r\n"
881 		"=  CR3=0x%016lx  CR4=0x%016lx\r\n"
882 		"=  RAX=0x%016lx  RBX=0x%016lx RCX=0x%016lx\r\n"
883 		"=  RDX=0x%016lx  RDI=0x%016lx RSI=0x%016lx\r\n"
884 		"=  RBP=0x%016lx  R8=0x%016lx R9=0x%016lx\r\n"
885 		"=  R10=0x%016lx  R11=0x%016lx R12=0x%016lx\r\n"
886 		"=  R13=0x%016lx  R14=0x%016lx  R15=0x%016lx\r\n",
887 		vcpu->vm->vm_id, vcpu->vcpu_id,
888 		vcpu_get_rip(vcpu),
889 		vcpu_get_gpreg(vcpu, CPU_REG_RSP),
890 		vcpu_get_rflags(vcpu),
891 		vcpu_get_cr0(vcpu), vcpu_get_cr2(vcpu),
892 		exec_vmread(VMX_GUEST_CR3), vcpu_get_cr4(vcpu),
893 		vcpu_get_gpreg(vcpu, CPU_REG_RAX),
894 		vcpu_get_gpreg(vcpu, CPU_REG_RBX),
895 		vcpu_get_gpreg(vcpu, CPU_REG_RCX),
896 		vcpu_get_gpreg(vcpu, CPU_REG_RDX),
897 		vcpu_get_gpreg(vcpu, CPU_REG_RDI),
898 		vcpu_get_gpreg(vcpu, CPU_REG_RSI),
899 		vcpu_get_gpreg(vcpu, CPU_REG_RBP),
900 		vcpu_get_gpreg(vcpu, CPU_REG_R8),
901 		vcpu_get_gpreg(vcpu, CPU_REG_R9),
902 		vcpu_get_gpreg(vcpu, CPU_REG_R10),
903 		vcpu_get_gpreg(vcpu, CPU_REG_R11),
904 		vcpu_get_gpreg(vcpu, CPU_REG_R12),
905 		vcpu_get_gpreg(vcpu, CPU_REG_R13),
906 		vcpu_get_gpreg(vcpu, CPU_REG_R14),
907 		vcpu_get_gpreg(vcpu, CPU_REG_R15));
908 	if (len >= size) {
909 		goto overflow;
910 	}
911 	size -= len;
912 	str += len;
913 
914 	/* dump sp */
915 	status = copy_from_gva(vcpu, tmp, vcpu_get_gpreg(vcpu, CPU_REG_RSP),
916 			DUMPREG_SP_SIZE*sizeof(uint64_t), &err_code,
917 			&fault_addr);
918 	if (status < 0) {
919 		/* copy_from_gva fail */
920 		len = snprintf(str, size, "Cannot handle user gva yet!\r\n");
921 		if (len >= size) {
922 			goto overflow;
923 		}
924 		size -= len;
925 		str += len;
926 	} else {
927 		len = snprintf(str, size, "\r\nDump RSP for vm %hu, from gva 0x%016lx\r\n",
928 			vcpu->vm->vm_id, vcpu_get_gpreg(vcpu, CPU_REG_RSP));
929 		if (len >= size) {
930 			goto overflow;
931 		}
932 		size -= len;
933 		str += len;
934 
935 		for (i = 0UL; i < 8UL; i++) {
936 			len = snprintf(str, size, "=  0x%016lx  0x%016lx 0x%016lx  0x%016lx\r\n",
937 					tmp[i*4UL], tmp[(i*4UL)+1UL], tmp[(i*4UL)+2UL], tmp[(i*4UL)+3UL]);
938 			if (len >= size) {
939 				goto overflow;
940 			}
941 			size -= len;
942 			str += len;
943 		}
944 	}
945 	if (curr != NULL) {
946 		load_vmcs(curr);
947 	}
948 	return;
949 
950 overflow:
951 	printf("buffer size could not be enough! please check!\n");
952 }
953 
shell_vcpu_dumpreg(int32_t argc,char ** argv)954 static int32_t shell_vcpu_dumpreg(int32_t argc, char **argv)
955 {
956 	int32_t status = 0;
957 	uint16_t vm_id;
958 	uint16_t vcpu_id, pcpu_id;
959 	struct acrn_vm *vm;
960 	struct acrn_vcpu *vcpu;
961 	uint64_t mask = 0UL;
962 	struct vcpu_dump dump;
963 
964 	/* User input invalidation */
965 	if (argc != 3) {
966 		shell_puts("Please enter cmd with <vm_id, vcpu_id>\r\n");
967 		status = -EINVAL;
968 		goto out;
969 	}
970 
971 	status = strtol_deci(argv[1]);
972 	if (status < 0) {
973 		goto out;
974 	}
975 	vm_id = sanitize_vmid((uint16_t)status);
976 	vcpu_id = (uint16_t)strtol_deci(argv[2]);
977 
978 	vm = get_vm_from_vmid(vm_id);
979 	if (is_poweroff_vm(vm)) {
980 		shell_puts("No vm found in the input <vm_id, vcpu_id>\r\n");
981 		status = -EINVAL;
982 		goto out;
983 	}
984 
985 	if (vcpu_id >= vm->hw.created_vcpus) {
986 		shell_puts("vcpu id is out of range\r\n");
987 		status = -EINVAL;
988 		goto out;
989 	}
990 
991 	vcpu = vcpu_from_vid(vm, vcpu_id);
992 	if (vcpu->state == VCPU_OFFLINE) {
993 		shell_puts("vcpu is offline\r\n");
994 		status = -EINVAL;
995 		goto out;
996 	}
997 
998 	pcpu_id = pcpuid_from_vcpu(vcpu);
999 	dump.vcpu = vcpu;
1000 	dump.str = shell_log_buf;
1001 	dump.str_max = SHELL_LOG_BUF_SIZE;
1002 	bitmap_set_nolock(pcpu_id, &mask);
1003 	smp_call_function(mask, dump_vcpu_reg, &dump);
1004 	shell_puts(shell_log_buf);
1005 	status = 0;
1006 
1007 out:
1008 	return status;
1009 }
1010 
shell_dump_host_mem(int32_t argc,char ** argv)1011 static int32_t shell_dump_host_mem(int32_t argc, char **argv)
1012 {
1013 	uint64_t *hva;
1014 	int32_t ret;
1015 	uint32_t i, length, loop_cnt;
1016 	char temp_str[MAX_STR_SIZE];
1017 
1018 	/* User input invalidation */
1019 	if (argc != 3) {
1020 		ret = -EINVAL;
1021 	} else	{
1022 		hva = (uint64_t *)strtoul_hex(argv[1]);
1023 		length = (uint32_t)strtol_deci(argv[2]);
1024 
1025 		snprintf(temp_str, MAX_STR_SIZE, "Dump physical memory addr: 0x%016lx, length %d:\r\n", hva, length);
1026 		shell_puts(temp_str);
1027 		/* Change the length to a multiple of 32 if the length is not */
1028 		loop_cnt = ((length & 0x1fU) == 0U) ? ((length >> 5U)) : ((length >> 5U) + 1U);
1029 		for (i = 0U; i < loop_cnt; i++) {
1030 			snprintf(temp_str, MAX_STR_SIZE, "HVA(0x%llx): 0x%016lx  0x%016lx  0x%016lx  0x%016lx\r\n",
1031 					hva, *hva, *(hva + 1UL), *(hva + 2UL), *(hva + 3UL));
1032 			hva += 4UL;
1033 			shell_puts(temp_str);
1034 		}
1035 		ret = 0;
1036 	}
1037 
1038 	return ret;
1039 }
1040 
dump_guest_mem(void * data)1041 static void dump_guest_mem(void *data)
1042 {
1043 	uint64_t i, fault_addr;
1044 	uint32_t err_code = 0;
1045 	uint64_t loop_cnt;
1046 	uint64_t buf[4];
1047 	char temp_str[MAX_STR_SIZE];
1048 	struct guest_mem_dump *dump = (struct guest_mem_dump *)data;
1049 	uint64_t length = dump->len;
1050 	uint64_t gva = dump->gva;
1051 	struct acrn_vcpu *vcpu = dump->vcpu;
1052 	uint16_t pcpu_id = get_pcpu_id();
1053 	struct acrn_vcpu *curr = get_running_vcpu(pcpu_id);
1054 
1055 	load_vmcs(vcpu);
1056 
1057 	/* Change the length to a multiple of 32 if the length is not */
1058 	loop_cnt = ((length & 0x1fUL) == 0UL) ? ((length >> 5UL)) : ((length >> 5UL) + 1UL);
1059 
1060 	for (i = 0UL; i < loop_cnt; i++) {
1061 
1062 		if (copy_from_gva(vcpu, buf, gva, 32U, &err_code, &fault_addr) != 0) {
1063 			printf("copy_from_gva error! err_code=0x%x fault_addr=0x%llx\r\n", err_code, fault_addr);
1064 			break;
1065 		}
1066 		snprintf(temp_str, MAX_STR_SIZE, "GVA(0x%llx):  0x%016lx  0x%016lx  0x%016lx  0x%016lx\r\n",
1067 				gva, buf[0], buf[1], buf[2], buf[3]);
1068 		shell_puts(temp_str);
1069 		gva += 32UL;
1070 	}
1071 	if (curr != NULL) {
1072 		load_vmcs(curr);
1073 	}
1074 }
1075 
shell_dump_guest_mem(int32_t argc,char ** argv)1076 static int32_t shell_dump_guest_mem(int32_t argc, char **argv)
1077 {
1078 	uint16_t vm_id, pcpu_id;
1079 	int32_t ret;
1080 	uint64_t gva;
1081 	uint64_t length;
1082 	uint64_t mask = 0UL;
1083 	struct acrn_vm *vm;
1084 	struct acrn_vcpu *vcpu = NULL;
1085 	struct guest_mem_dump dump;
1086 
1087 	/* User input invalidation */
1088 	if (argc != 4) {
1089 		ret = -EINVAL;
1090 	} else {
1091 		vm_id = sanitize_vmid((uint16_t)strtol_deci(argv[1]));
1092 		gva = strtoul_hex(argv[2]);
1093 		length = (uint64_t)strtol_deci(argv[3]);
1094 
1095 		vm = get_vm_from_vmid(vm_id);
1096 		vcpu = vcpu_from_vid(vm, BSP_CPU_ID);
1097 
1098 		dump.vcpu = vcpu;
1099 		dump.gva = gva;
1100 		dump.len = length;
1101 
1102 		pcpu_id = pcpuid_from_vcpu(vcpu);
1103 		bitmap_set_nolock(pcpu_id, &mask);
1104 		smp_call_function(mask, dump_guest_mem, &dump);
1105 		ret = 0;
1106 	}
1107 
1108 	return ret;
1109 }
1110 
shell_to_vm_console(int32_t argc,char ** argv)1111 static int32_t shell_to_vm_console(int32_t argc, char **argv)
1112 {
1113 	char temp_str[TEMP_STR_SIZE];
1114 	uint16_t vm_id = 0U;
1115 
1116 	struct acrn_vm *vm;
1117 	struct acrn_vuart *vu;
1118 
1119 	if (argc == 2) {
1120 		vm_id = sanitize_vmid((uint16_t)strtol_deci(argv[1]));
1121 	}
1122 
1123 	/* Get the virtual device node */
1124 	vm = get_vm_from_vmid(vm_id);
1125 	if (is_poweroff_vm(vm)) {
1126 		shell_puts("VM is not valid \n");
1127 		return -EINVAL;
1128 	}
1129 	vu = vm_console_vuart(vm);
1130 	if (!vu->active) {
1131 		shell_puts("vuart console is not active \n");
1132 		return 0;
1133 	}
1134 	console_vmid = vm_id;
1135 	/* Output that switching to Service VM shell */
1136 	snprintf(temp_str, TEMP_STR_SIZE, "\r\n----- Entering VM %d Shell -----\r\n", vm_id);
1137 
1138 	shell_puts(temp_str);
1139 
1140 	return 0;
1141 }
1142 
1143 /**
1144  * @brief Get the interrupt statistics
1145  *
1146  * It's for debug only.
1147  *
1148  * @param[in]	str_max	The max size of the string containing interrupt info
1149  * @param[inout]	str_arg	Pointer to the output interrupt info
1150  */
get_cpu_interrupt_info(char * str_arg,size_t str_max)1151 static void get_cpu_interrupt_info(char *str_arg, size_t str_max)
1152 {
1153 	char *str = str_arg;
1154 	uint16_t pcpu_id;
1155 	uint32_t irq, vector;
1156 	size_t len, size = str_max;
1157 	uint16_t pcpu_nums = get_pcpu_nums();
1158 
1159 	len = snprintf(str, size, "\r\nIRQ\tVECTOR");
1160 	if (len >= size) {
1161 		goto overflow;
1162 	}
1163 	size -= len;
1164 	str += len;
1165 
1166 	for (pcpu_id = 0U; pcpu_id < pcpu_nums; pcpu_id++) {
1167 		len = snprintf(str, size, "\tCPU%d", pcpu_id);
1168 		if (len >= size) {
1169 			goto overflow;
1170 		}
1171 		size -= len;
1172 		str += len;
1173 	}
1174 
1175 	for (irq = 0U; irq < NR_IRQS; irq++) {
1176 		vector = irq_to_vector(irq);
1177 		if (bitmap_test((uint16_t)(irq & 0x3FU),
1178 			irq_alloc_bitmap + (irq >> 6U))
1179 			&& (vector != VECTOR_INVALID)) {
1180 			len = snprintf(str, size, "\r\n%d\t0x%X", irq, vector);
1181 			if (len >= size) {
1182 				goto overflow;
1183 			}
1184 			size -= len;
1185 			str += len;
1186 
1187 			for (pcpu_id = 0U; pcpu_id < pcpu_nums; pcpu_id++) {
1188 				len = snprintf(str, size, "\t%d", per_cpu(irq_count, pcpu_id)[irq]);
1189 				if (len >= size) {
1190 					goto overflow;
1191 				}
1192 				size -= len;
1193 				str += len;
1194 			}
1195 		}
1196 	}
1197 	snprintf(str, size, "\r\n");
1198 	return;
1199 
1200 overflow:
1201 	printf("buffer size could not be enough! please check!\n");
1202 }
1203 
shell_show_cpu_int(__unused int32_t argc,__unused char ** argv)1204 static int32_t shell_show_cpu_int(__unused int32_t argc, __unused char **argv)
1205 {
1206 	get_cpu_interrupt_info(shell_log_buf, SHELL_LOG_BUF_SIZE);
1207 	shell_puts(shell_log_buf);
1208 	return 0;
1209 }
1210 
get_entry_info(const struct ptirq_remapping_info * entry,char * type,uint32_t * irq,uint32_t * vector,uint64_t * dest,bool * lvl_tm,uint32_t * pgsi,uint32_t * vgsi,union pci_bdf * bdf,union pci_bdf * vbdf)1211 static void get_entry_info(const struct ptirq_remapping_info *entry, char *type,
1212 		uint32_t *irq, uint32_t *vector, uint64_t *dest, bool *lvl_tm,
1213 		uint32_t *pgsi, uint32_t *vgsi, union pci_bdf *bdf, union pci_bdf *vbdf)
1214 {
1215 	if (is_entry_active(entry)) {
1216 		if (entry->intr_type == PTDEV_INTR_MSI) {
1217 			(void)strncpy_s(type, 16U, "MSI", 16U);
1218 			*dest = entry->pmsi.addr.bits.dest_field;
1219 			if (entry->pmsi.data.bits.trigger_mode == MSI_DATA_TRGRMODE_LEVEL) {
1220 				*lvl_tm = true;
1221 			} else {
1222 				*lvl_tm = false;
1223 			}
1224 			*pgsi = INVALID_INTERRUPT_PIN;
1225 			*vgsi = INVALID_INTERRUPT_PIN;
1226 			bdf->value = entry->phys_sid.msi_id.bdf;
1227 			vbdf->value = entry->virt_sid.msi_id.bdf;
1228 		} else {
1229 			uint32_t phys_irq = entry->allocated_pirq;
1230 			union ioapic_rte rte;
1231 
1232 			if (entry->virt_sid.intx_id.ctlr == INTX_CTLR_IOAPIC) {
1233 				(void)strncpy_s(type, 16U, "IOAPIC", 16U);
1234 			} else {
1235 				(void)strncpy_s(type, 16U, "PIC", 16U);
1236 			}
1237 			ioapic_get_rte(phys_irq, &rte);
1238 			*dest = rte.bits.dest_field;
1239 			if (rte.bits.trigger_mode == IOAPIC_RTE_TRGRMODE_LEVEL) {
1240 				*lvl_tm = true;
1241 			} else {
1242 				*lvl_tm = false;
1243 			}
1244 			*pgsi = entry->phys_sid.intx_id.gsi;
1245 			*vgsi = entry->virt_sid.intx_id.gsi;
1246 			bdf->value = 0U;
1247 			vbdf->value = 0U;
1248 		}
1249 		*irq = entry->allocated_pirq;
1250 		*vector = irq_to_vector(entry->allocated_pirq);
1251 	} else {
1252 		(void)strncpy_s(type, 16U, "NONE", 16U);
1253 		*irq = IRQ_INVALID;
1254 		*vector = 0U;
1255 		*dest = 0UL;
1256 		*lvl_tm = 0;
1257 		*pgsi = ~0U;
1258 		*vgsi = ~0U;
1259 		bdf->value = 0U;
1260 		vbdf->value = 0U;
1261 	}
1262 }
1263 
get_ptdev_info(char * str_arg,size_t str_max)1264 static void get_ptdev_info(char *str_arg, size_t str_max)
1265 {
1266 	char *str = str_arg;
1267 	struct ptirq_remapping_info *entry;
1268 	uint16_t idx;
1269 	size_t len, size = str_max;
1270 	uint32_t irq, vector;
1271 	char type[16];
1272 	uint64_t dest;
1273 	bool lvl_tm;
1274 	uint32_t pgsi, vgsi;
1275 	union pci_bdf bdf, vbdf;
1276 
1277 	len = snprintf(str, size, "\r\nVM\tTYPE\tIRQ\tVEC\tDEST\tTM\tGSI\tVGSI\tBDF\tVBDF");
1278 	if (len >= size) {
1279 		goto overflow;
1280 	}
1281 	size -= len;
1282 	str += len;
1283 
1284 	for (idx = 0U; idx < CONFIG_MAX_PT_IRQ_ENTRIES; idx++) {
1285 		entry = &ptirq_entries[idx];
1286 		if (is_entry_active(entry)) {
1287 			get_entry_info(entry, type, &irq, &vector, &dest, &lvl_tm, &pgsi, &vgsi,
1288 					&bdf, &vbdf);
1289 			len = snprintf(str, size, "\r\n%d\t%s\t%d\t0x%X\t0x%X",
1290 					entry->vm->vm_id, type, irq, vector, dest);
1291 			if (len >= size) {
1292 				goto overflow;
1293 			}
1294 			size -= len;
1295 			str += len;
1296 
1297 			len = snprintf(str, size, "\t%s\t%hhu\t%hhu\t%x:%x.%x\t%x:%x.%x",
1298 					is_entry_active(entry) ? (lvl_tm ? "level" : "edge") : "none",
1299 					pgsi, vgsi, bdf.bits.b, bdf.bits.d, bdf.bits.f,
1300 					vbdf.bits.b, vbdf.bits.d, vbdf.bits.f);
1301 			if (len >= size) {
1302 				goto overflow;
1303 			}
1304 			size -= len;
1305 			str += len;
1306 		}
1307 	}
1308 
1309 	snprintf(str, size, "\r\n");
1310 	return;
1311 
1312 overflow:
1313 	printf("buffer size could not be enough! please check!\n");
1314 }
1315 
shell_show_ptdev_info(__unused int32_t argc,__unused char ** argv)1316 static int32_t shell_show_ptdev_info(__unused int32_t argc, __unused char **argv)
1317 {
1318 	get_ptdev_info(shell_log_buf, SHELL_LOG_BUF_SIZE);
1319 	shell_puts(shell_log_buf);
1320 
1321 	return 0;
1322 }
1323 
get_vioapic_info(char * str_arg,size_t str_max,uint16_t vmid)1324 static void get_vioapic_info(char *str_arg, size_t str_max, uint16_t vmid)
1325 {
1326 	char *str = str_arg;
1327 	size_t len, size = str_max;
1328 	union ioapic_rte rte;
1329 	uint32_t delmode, vector, dest;
1330 	bool level, phys, remote_irr, mask;
1331 	struct acrn_vm *vm = get_vm_from_vmid(vmid);
1332 	uint32_t gsi, gsi_count;
1333 
1334 	if (is_poweroff_vm(vm)) {
1335 		len = snprintf(str, size, "\r\nvm is not exist for vmid %hu", vmid);
1336 		if (len >= size) {
1337 			goto overflow;
1338 		}
1339 		size -= len;
1340 		str += len;
1341 		goto END;
1342 	}
1343 
1344 	len = snprintf(str, size, "\r\nPIN\tVEC\tDM\tDEST\tTM\tDELM\tIRR\tMASK");
1345 	if (len >= size) {
1346 		goto overflow;
1347 	}
1348 	size -= len;
1349 	str += len;
1350 
1351 	gsi_count = get_vm_gsicount(vm);
1352 	rte.full = 0UL;
1353 	for (gsi = 0U; gsi < gsi_count; gsi++) {
1354 		if (is_service_vm(vm) && (!is_gsi_valid(gsi))) {
1355 			continue;
1356 		}
1357 		vioapic_get_rte(vm, gsi, &rte);
1358 		mask = (rte.bits.intr_mask == IOAPIC_RTE_MASK_SET);
1359 		remote_irr = (rte.bits.remote_irr == IOAPIC_RTE_REM_IRR);
1360 		phys = (rte.bits.dest_mode == IOAPIC_RTE_DESTMODE_PHY);
1361 		delmode = rte.bits.delivery_mode;
1362 		level = (rte.bits.trigger_mode == IOAPIC_RTE_TRGRMODE_LEVEL);
1363 		vector = rte.bits.vector;
1364 		dest = rte.bits.dest_field;
1365 
1366 		len = snprintf(str, size, "\r\n%hhu\t0x%X\t%s\t0x%X\t%s\t%u\t%d\t%d",
1367 				gsi, vector, phys ? "phys" : "logic", dest, level ? "level" : "edge",
1368 				delmode >> 8U, remote_irr, mask);
1369 		if (len >= size) {
1370 			goto overflow;
1371 		}
1372 		size -= len;
1373 		str += len;
1374 	}
1375 END:
1376 	snprintf(str, size, "\r\n");
1377 	return;
1378 
1379 overflow:
1380 	printf("buffer size could not be enough! please check!\n");
1381 }
1382 
shell_show_vioapic_info(int32_t argc,char ** argv)1383 static int32_t shell_show_vioapic_info(int32_t argc, char **argv)
1384 {
1385 	uint16_t vmid;
1386 	int32_t ret;
1387 
1388 	/* User input invalidation */
1389 	if (argc != 2) {
1390 		return -EINVAL;
1391 	}
1392 	ret = strtol_deci(argv[1]);
1393 	if (ret >= 0) {
1394 		vmid = sanitize_vmid((uint16_t) ret);
1395 		get_vioapic_info(shell_log_buf, SHELL_LOG_BUF_SIZE, vmid);
1396 		shell_puts(shell_log_buf);
1397 		return 0;
1398 	}
1399 
1400 	return -EINVAL;
1401 }
1402 
1403 /**
1404  * @brief Get information of ioapic
1405  *
1406  * It's for debug only.
1407  *
1408  * @param[in]	str_max_len	The max size of the string containing
1409  *				interrupt info
1410  * @param[inout]	str_arg	Pointer to the output information
1411  */
get_ioapic_info(char * str_arg,size_t str_max_len)1412 static int32_t get_ioapic_info(char *str_arg, size_t str_max_len)
1413 {
1414 	char *str = str_arg;
1415 	uint32_t gsi;
1416 	size_t len, size = str_max_len;
1417 	uint32_t ioapic_nr_gsi = 0U;
1418 
1419 	len = snprintf(str, size, "\r\nIRQ\tPIN\tRTE.HI32\tRTE.LO32\tVEC\tDST\tDM\tTM\tDELM\tIRR\tMASK");
1420 	if (len >= size) {
1421 		goto overflow;
1422 	}
1423 	size -= len;
1424 	str += len;
1425 
1426 	ioapic_nr_gsi = get_max_nr_gsi ();
1427 	for (gsi = 0U; gsi < ioapic_nr_gsi; gsi++) {
1428 		void *addr;
1429 		uint32_t pin;
1430 		union ioapic_rte rte;
1431 
1432 		if (!is_gsi_valid(gsi)) {
1433 			continue;
1434 		}
1435 		addr = gsi_to_ioapic_base(gsi);
1436 		pin = gsi_to_ioapic_pin(gsi);
1437 
1438 		ioapic_get_rte_entry(addr, pin, &rte);
1439 
1440 		len = snprintf(str, size, "\r\n%03d\t%03hhu\t0x%08X\t0x%08X\t", gsi, pin, rte.u.hi_32, rte.u.lo_32);
1441 		if (len >= size) {
1442 			goto overflow;
1443 		}
1444 		size -= len;
1445 		str += len;
1446 
1447 		len = snprintf(str, size, "0x%02X\t0x%02X\t%s\t%s\t%u\t%d\t%d",
1448 			rte.bits.vector, rte.bits.dest_field,
1449 			(rte.bits.dest_mode == IOAPIC_RTE_DESTMODE_LOGICAL)? "logic" : "phys",
1450 			(rte.bits.trigger_mode == IOAPIC_RTE_TRGRMODE_LEVEL)? "level" : "edge",
1451 			rte.bits.delivery_mode, rte.bits.remote_irr,
1452 			rte.bits.intr_mask);
1453 		if (len >= size) {
1454 			goto overflow;
1455 		}
1456 		size -= len;
1457 		str += len;
1458 	}
1459 
1460 	snprintf(str, size, "\r\n");
1461 	return 0;
1462 
1463 overflow:
1464 	printf("buffer size could not be enough! please check!\n");
1465 	return 0;
1466 }
1467 
shell_show_ioapic_info(__unused int32_t argc,__unused char ** argv)1468 static int32_t shell_show_ioapic_info(__unused int32_t argc, __unused char **argv)
1469 {
1470 	int32_t err = 0;
1471 
1472 	err = get_ioapic_info(shell_log_buf, SHELL_LOG_BUF_SIZE);
1473 	shell_puts(shell_log_buf);
1474 
1475 	return err;
1476 }
1477 
shell_loglevel(int32_t argc,char ** argv)1478 static int32_t shell_loglevel(int32_t argc, char **argv)
1479 {
1480 	char str[MAX_STR_SIZE] = {0};
1481 
1482 	switch (argc) {
1483 	case 4:
1484 		npk_loglevel = (uint16_t)strtol_deci(argv[3]);
1485 		/* falls through */
1486 	case 3:
1487 		mem_loglevel = (uint16_t)strtol_deci(argv[2]);
1488 		/* falls through */
1489 	case 2:
1490 		console_loglevel = (uint16_t)strtol_deci(argv[1]);
1491 		break;
1492 	case 1:
1493 		snprintf(str, MAX_STR_SIZE, "console_loglevel: %u, "
1494 			"mem_loglevel: %u, npk_loglevel: %u\r\n",
1495 			console_loglevel, mem_loglevel, npk_loglevel);
1496 		shell_puts(str);
1497 		break;
1498 	default:
1499 		return -EINVAL;
1500 	}
1501 
1502 	return 0;
1503 }
1504 
shell_cpuid(int32_t argc,char ** argv)1505 static int32_t shell_cpuid(int32_t argc, char **argv)
1506 {
1507 	char str[MAX_STR_SIZE] = {0};
1508 	uint32_t leaf, subleaf = 0;
1509 	uint32_t eax, ebx, ecx, edx;
1510 
1511 	if (argc == 2) {
1512 		leaf = (uint32_t)strtoul_hex(argv[1]);
1513 	} else if (argc == 3) {
1514 		leaf = (uint32_t)strtoul_hex(argv[1]);
1515 		subleaf = (uint32_t)strtoul_hex(argv[2]);
1516 	} else {
1517 		shell_puts("Please enter correct cmd with "
1518 			"cpuid <leaf> [subleaf]\r\n");
1519 		return -EINVAL;
1520 	}
1521 
1522 	cpuid_subleaf(leaf, subleaf, &eax, &ebx, &ecx, &edx);
1523 	snprintf(str, MAX_STR_SIZE,
1524 		"cpuid leaf: 0x%x, subleaf: 0x%x, 0x%x:0x%x:0x%x:0x%x\r\n",
1525 		leaf, subleaf, eax, ebx, ecx, edx);
1526 
1527 	shell_puts(str);
1528 
1529 	return 0;
1530 }
1531 
shell_reboot(__unused int32_t argc,__unused char ** argv)1532 static int32_t shell_reboot(__unused int32_t argc, __unused char **argv)
1533 {
1534 	reset_host(false);
1535 	return 0;
1536 }
1537 
shell_rdmsr(int32_t argc,char ** argv)1538 static int32_t shell_rdmsr(int32_t argc, char **argv)
1539 {
1540 	uint16_t pcpu_id = 0;
1541 	int32_t ret = 0;
1542 	uint32_t msr_index = 0;
1543 	uint64_t val = 0;
1544 	char str[MAX_STR_SIZE] = {0};
1545 
1546 	pcpu_id = get_pcpu_id();
1547 
1548 	switch (argc) {
1549 	case 3:
1550 		/* rdrmsr -p<PCPU_ID> <MSR_INDEX>*/
1551 		 if ((argv[1][0] == '-') && (argv[1][1] == 'p')) {
1552 			pcpu_id = (uint16_t)strtol_deci(&(argv[1][2]));
1553 			msr_index = (uint32_t)strtoul_hex(argv[2]);
1554 		} else {
1555 			ret = -EINVAL;
1556 		}
1557 		break;
1558 	case 2:
1559 		/* rdmsr <MSR_INDEX> */
1560 		msr_index = (uint32_t)strtoul_hex(argv[1]);
1561 		break;
1562 	default:
1563 		ret = -EINVAL;
1564 	}
1565 
1566 	if (ret == 0) {
1567 		if (pcpu_id < get_pcpu_nums()) {
1568 			val = msr_read_pcpu(msr_index, pcpu_id);
1569 			snprintf(str, MAX_STR_SIZE, "rdmsr(0x%x):0x%lx\n", msr_index, val);
1570 			shell_puts(str);
1571 		} else {
1572 			shell_puts("pcpu id is out of range!\n");
1573 		}
1574 	}
1575 
1576 	return ret;
1577 }
1578 
shell_wrmsr(int32_t argc,char ** argv)1579 static int32_t shell_wrmsr(int32_t argc, char **argv)
1580 {
1581 	uint16_t pcpu_id = 0;
1582 	int32_t ret = 0;
1583 	uint32_t msr_index = 0;
1584 	uint64_t val = 0;
1585 
1586 	pcpu_id = get_pcpu_id();
1587 
1588 	switch (argc) {
1589 	case 4:
1590 		/* wrmsr -p<PCPU_ID> <MSR_INDEX> <VALUE>*/
1591 		 if ((argv[1][0] == '-') && (argv[1][1] == 'p')) {
1592 			pcpu_id = (uint16_t)strtol_deci(&(argv[1][2]));
1593 			msr_index = (uint32_t)strtoul_hex(argv[2]);
1594 			val = strtoul_hex(argv[3]);
1595 		} else {
1596 			ret = -EINVAL;
1597 		}
1598 		break;
1599 	case 3:
1600 		/* wrmsr <MSR_INDEX> <VALUE>*/
1601 		msr_index = (uint32_t)strtoul_hex(argv[1]);
1602 		val = strtoul_hex(argv[2]);
1603 		break;
1604 	default:
1605 		ret = -EINVAL;
1606 	}
1607 
1608 	if (ret == 0) {
1609 		if (pcpu_id < get_pcpu_nums()) {
1610 			msr_write_pcpu(msr_index, val, pcpu_id);
1611 		} else {
1612 			shell_puts("pcpu id is out of range!\n");
1613 		}
1614 	}
1615 
1616 	return ret;
1617 }
1618