From eb5a57b780f18402813be443f89df8d88625c287 Mon Sep 17 00:00:00 2001 From: Minggui Cao Date: Wed, 4 Sep 2019 17:40:09 +0800 Subject: [PATCH] DM: add guest vm power manager by vuart vuart can be used as communication channel between VMs; here vuart used to control vm's power off flow; control command is from SOS to UOS Tracked-On: #3564 Signed-off-by: Minggui Cao Acked-by: Yin Fengwei --- devicemodel/Makefile | 1 + devicemodel/core/main.c | 19 ++- devicemodel/core/pm_vuart.c | 208 +++++++++++++++++++++++++++++++++ devicemodel/include/pm_vuart.h | 18 +++ 4 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 devicemodel/core/pm_vuart.c create mode 100644 devicemodel/include/pm_vuart.h diff --git a/devicemodel/Makefile b/devicemodel/Makefile index 006694496..f29341436 100644 --- a/devicemodel/Makefile +++ b/devicemodel/Makefile @@ -141,6 +141,7 @@ SRCS += core/sw_load_elf.c SRCS += core/mevent.c SRCS += core/gc.c SRCS += core/pm.c +SRCS += core/pm_vuart.c SRCS += core/console.c SRCS += core/inout.c SRCS += core/mem.c diff --git a/devicemodel/core/main.c b/devicemodel/core/main.c index 417dc1d7f..7de54709b 100644 --- a/devicemodel/core/main.c +++ b/devicemodel/core/main.c @@ -63,6 +63,7 @@ #include "atomic.h" #include "tpm.h" #include "virtio.h" +#include "pm_vuart.h" #include "log.h" #define GUEST_NIO_PORT 0x488 /* guest upcalls via i/o port */ @@ -138,7 +139,8 @@ usage(int code) " %*s [--part_info part_info_name] [--enable_trusty] [--intr_monitor param_setting]\n" " %*s [--vtpm2 sock_path] [--virtio_poll interval] [--mac_seed seed_string]\n" " %*s [--vmcfg sub_options] [--dump vm_idx] [--ptdev_no_reset] [--debugexit] \n" - " %*s [--logger-setting param_setting] [--pm_notify_channel] \n" + " %*s [--logger-setting param_setting] [--pm_notify_channel]\n" + " %*s [--pm_by_vuart vuart_node] \n" " -A: create ACPI tables\n" " -B: bootargs for kernel\n" " -c: # cpus (default 1)\n" @@ -173,11 +175,12 @@ usage(int code) " --lapic_pt: enable local apic passthrough\n" " --rtvm: indicate that the guest is rtvm\n" " --logger_setting: params like console,level=4;kmsg,level=3\n" - " --pm_notify_channel: define the channel used to notify guest about power event\n", + " --pm_notify_channel: define the channel used to notify guest about power event\n" + " --pm_by_vuart:pty,/run/acrn/vuart_vmname or tty,/dev/ttySn\n", progname, (int)strnlen(progname, PATH_MAX), "", (int)strnlen(progname, PATH_MAX), "", (int)strnlen(progname, PATH_MAX), "", (int)strnlen(progname, PATH_MAX), "", (int)strnlen(progname, PATH_MAX), "", (int)strnlen(progname, PATH_MAX), "", - (int)strnlen(progname, PATH_MAX), ""); + (int)strnlen(progname, PATH_MAX), "", (int)strnlen(progname, PATH_MAX), ""); exit(code); } @@ -421,6 +424,8 @@ guest_pm_notify_init(struct vmctx *ctx) ioc_init(ctx); else if (PWR_EVENT_NOTIFY_PWR_BT == pm_notify_channel) power_button_init(ctx); + else if (PWR_EVENT_NOTIFY_UART == pm_notify_channel) + pm_by_vuart_init(ctx); else pr_err("No correct pm notify channel given\n"); } @@ -432,6 +437,8 @@ guest_pm_notify_deinit(struct vmctx *ctx) ioc_deinit(ctx); else if (PWR_EVENT_NOTIFY_PWR_BT == pm_notify_channel) power_button_deinit(ctx); + else if (PWR_EVENT_NOTIFY_UART == pm_notify_channel) + pm_by_vuart_deinit(ctx); else pr_err("No correct pm notify channel given\n"); } @@ -724,6 +731,7 @@ enum { CMD_OPT_RTVM, CMD_OPT_LOGGER_SETTING, CMD_OPT_PM_NOTIFY_CHANNEL, + CMD_OPT_PM_BY_VUART, }; static struct option long_options[] = { @@ -763,6 +771,7 @@ static struct option long_options[] = { {"rtvm", no_argument, 0, CMD_OPT_RTVM}, {"logger_setting", required_argument, 0, CMD_OPT_LOGGER_SETTING}, {"pm_notify_channel", required_argument, 0, CMD_OPT_PM_NOTIFY_CHANNEL}, + {"pm_by_vuart", required_argument, 0, CMD_OPT_PM_BY_VUART}, {0, 0, 0, 0 }, }; @@ -924,6 +933,10 @@ main(int argc, char *argv[]) pm_notify_channel = PWR_EVENT_NOTIFY_UART; break; + case CMD_OPT_PM_BY_VUART: + if (parse_pm_by_vuart(optarg) != 0) + errx(EX_USAGE, "invalid pm-by-vuart params %s", optarg); + break; case 'h': usage(0); default: diff --git a/devicemodel/core/pm_vuart.c b/devicemodel/core/pm_vuart.c new file mode 100644 index 000000000..9d9365d66 --- /dev/null +++ b/devicemodel/core/pm_vuart.c @@ -0,0 +1,208 @@ +/* + * Project Acrn + * Acrn-dm: pm-vuart + * + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +/* vuart can be used communication between SOS and UOS, here it is used as power manager control. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vmmapi.h" +#include "monitor.h" +#include "pty_vuart.h" +#include "log.h" + +#define SHUTDOWN_UOS_CMD "shutdown" +#define SHUTDOWN_CMD_ACK "acked" +#define CMD_LEN 16 +#define WAIT_SND_CNT 3 +#define WAIT_ACK_CNT 5 +#define MAX_NODE_PATH 128 + +static const char * const node_name[] = { + "pty", + "tty", +}; + +enum node_type_t { + PTY_NODE, + TTY_NODE, + MAX_NODE_CNT, +}; + +static uint8_t node_index = MAX_NODE_CNT; +static char node_path[MAX_NODE_PATH]; +static int node_fd = -1; +static bool shutdown_uos_thread_started = false; +static pthread_t shutdown_uos_thread_pid; + +static int vm_stop_handler(void *arg); +static struct monitor_vm_ops vm_ops = { + .stop = vm_stop_handler, +}; + +/* + * --pm_vuart configuration is in the following 2 forms: + * A: pty-link, like: pty,/run/acrn/vuart-vm1, (also set it in -l com2,/run/acrn/vuart-vm1) + * the SOS and UOS will communicate by: SOS:pty-link-node <--> SOS:com2 <--> UOS: /dev/ttyS1 + * B: tty-node, like: tty,/dev/ttyS1, SOS and UOS communicate by: SOS:ttyS1 <--> HV <-->UOS:ttySn + */ +int parse_pm_by_vuart(const char *opts) +{ + int i, error = -1; + char *str, *cpy, *type; + + str = cpy = strdup(opts); + type = strsep(&str, ","); + + if (type != NULL) { + for (i = 0; i < MAX_NODE_CNT; i++) { + if (strcasecmp(type, node_name[i]) == 0) { + node_index = i; + error = 0; + break; + } + } + } + + pr_info("pm by vuart node-index = %d\n", node_index); + strncpy(node_path, str, MAX_NODE_PATH - 1); + + free(cpy); + return error; +} + +void pm_by_vuart_init(struct vmctx *ctx) +{ + assert(node_index < MAX_NODE_CNT); + + if (node_index == PTY_NODE) { + node_fd = pty_open_virtual_uart(node_path); + } else if (node_index == TTY_NODE) { + node_fd = open(node_path, O_RDWR | O_NOCTTY | O_NONBLOCK); + } + + if (node_fd > 0) { + if (monitor_register_vm_ops(&vm_ops, ctx, "pm-vuart") < 0) { + pr_err("%s: pm-vuart register to VM monitor failed\n", node_path); + close(node_fd); + node_fd = -1; + } + } else { + pr_err("%s open failed, fd=%d\n", node_path, node_fd); + } +} + +void pm_by_vuart_deinit(struct vmctx *ctx) +{ + close(node_fd); + node_fd = -1; +} + +/* it read from vuart, and if end is '\0' or '\n' or len = buff-len it will return */ +static int read_bytes(int fd, uint8_t *buffer, int buf_len) +{ + int rc, offset = 0; + + do { + rc = read(fd, buffer + offset, buf_len - offset); + if (rc > 0) { + offset += rc; + + if ((buffer[offset - 1] == '\0') || (buffer[offset - 1] == '\n') || + (offset == buf_len)) { + break; + } + } + } while (rc > 0); + + return offset; +} + +/* thread to send shutdown cmd to uos and wait acked from it */ +static void *shutdown_uos_thread(void *arg) +{ + int rc, wait_snd, wait_ack; + char buffer[CMD_LEN]; + + wait_snd = WAIT_SND_CNT; + while (wait_snd > 0) { + rc = write(node_fd, SHUTDOWN_UOS_CMD, strlen(SHUTDOWN_UOS_CMD)); + if (rc < 0) { + pr_err("send shutdown cmd to uos failed!\n"); + break; + } + + sleep(1); /* wait 1 second to communicate with UOS */ + + wait_ack = WAIT_ACK_CNT; + while (wait_ack > 0) { + rc = read_bytes(node_fd, (uint8_t *)buffer, CMD_LEN - 1); + if ((rc > 0) && strncmp(buffer, SHUTDOWN_CMD_ACK, strlen(SHUTDOWN_CMD_ACK)) == 0) { + pr_info("received acked from UOS\n"); + break; + } + + sleep(1); + wait_ack--; + } + + if (wait_ack > 0) { + break; + } + + wait_snd--; + } + + if ((wait_snd == 0) && (wait_ack == 0)) { + pr_err("not received acked from UOS\n"); + } + + shutdown_uos_thread_started = false; + return NULL; +} + +/* called when acrn-dm receive stop command */ +static int vm_stop_handler(void *arg) +{ + int ret; + + pr_info("pm-vuart stop handler called: node-index=%d\n", node_index); + assert(node_index < MAX_NODE_CNT); + + if (node_fd <= 0) { + pr_err("no vuart node opened!\n"); + return -1; + } + + if (shutdown_uos_thread_started) { + pr_info("stop cmd send thread started!\n"); + return -1; + } + + shutdown_uos_thread_started = true; + ret = pthread_create(&shutdown_uos_thread_pid, NULL, shutdown_uos_thread, NULL); + if (ret) { + pr_err("failed %s %d\n", __func__, __LINE__); + shutdown_uos_thread_pid = 0; + shutdown_uos_thread_started = false; + return -1; + } + + pr_info("created shutdown uos thread.\n"); + pthread_setname_np(shutdown_uos_thread_pid, "shutdown_uos"); + + return 0; +} diff --git a/devicemodel/include/pm_vuart.h b/devicemodel/include/pm_vuart.h new file mode 100644 index 000000000..0956f7ba7 --- /dev/null +++ b/devicemodel/include/pm_vuart.h @@ -0,0 +1,18 @@ +/* + * Project Acrn + * Acrn-dm: pm-vuart + * + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#ifndef __PM_VUART__ +#define __PM_VUART__ + +int parse_pm_by_vuart(const char *opts); +void pm_by_vuart_init(struct vmctx *ctx); +void pm_by_vuart_deinit(struct vmctx *ctx); + +#endif