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