diff --git a/devicemodel/core/main.c b/devicemodel/core/main.c index 263627ca2..828fdd23c 100644 --- a/devicemodel/core/main.c +++ b/devicemodel/core/main.c @@ -516,6 +516,12 @@ vrtc_fail: static void vm_deinit_vdevs(struct vmctx *ctx) { + /* + * Write ovmf NV storage back to the original file from guest + * memory before deinit operations. + */ + acrn_writeback_ovmf_nvstorage(ctx); + deinit_pci(ctx); monitor_close(); @@ -535,6 +541,12 @@ vm_deinit_vdevs(struct vmctx *ctx) static void vm_reset_vdevs(struct vmctx *ctx) { + /* + * Write ovmf NV storage back to the original file from guest + * memory before deinit operations. + */ + acrn_writeback_ovmf_nvstorage(ctx); + /* * The current virtual devices doesn't define virtual * device reset function. So we call vdev deinit/init diff --git a/devicemodel/core/sw_load_ovmf.c b/devicemodel/core/sw_load_ovmf.c index bd04447ff..d6f167ce3 100644 --- a/devicemodel/core/sw_load_ovmf.c +++ b/devicemodel/core/sw_load_ovmf.c @@ -48,17 +48,27 @@ * | | * + NV data storage + * | | - * +--------------------------------------------------+ + * +--------------------------------------------------+ <--OVMF offset 0 */ /* ovmf real entry is reset vector, which is (OVMF_TOP - 16) */ #define OVMF_TOP(ctx) (4*GB) +/* ovmf NV storage begins at offset 0 */ +#define OVMF_NVSTORAGE_OFFSET (OVMF_TOP(ctx) - ovmf_size) + +/* ovmf image size limit */ +#define OVMF_SZ_LIMIT (2*MB) + +/* ovmf NV storage size */ +#define OVMF_NVSTORAGE_SZ (128*KB) + /* located in the ROM area */ #define OVMF_E820_BASE 0x000EF000UL static char ovmf_path[STR_LEN]; static size_t ovmf_size; +bool writeback_nv_storage; extern int init_cmos_vrpmb(struct vmctx *ctx); @@ -72,17 +82,30 @@ int acrn_parse_ovmf(char *arg) { int error = -1; + char *str, *cp, *token = NULL; size_t len = strnlen(arg, STR_LEN); + str = strdup(arg); if (len < STR_LEN) { - strncpy(ovmf_path, arg, len + 1); - if (check_image(ovmf_path, 2 * MB, &ovmf_size) == 0) { - ovmf_file_name = ovmf_path; - printf("SW_LOAD: get ovmf path %s, size 0x%lx\n", - ovmf_path, ovmf_size); - error = 0; + cp = str; + token = strsep(&cp, ","); + while (token != NULL) { + if (strcmp(token, "w") == 0) { + writeback_nv_storage = true; + } else { + len = strnlen(token, STR_LEN); + strncpy(ovmf_path, token, len + 1); + if (check_image(ovmf_path, OVMF_SZ_LIMIT, &ovmf_size) != 0) + break; + ovmf_file_name = ovmf_path; + printf("SW_LOAD: get ovmf path %s, size 0x%lx\n", + ovmf_path, ovmf_size); + error = 0; + } + token = strsep(&cp, ","); } } + free(str); return error; } @@ -123,7 +146,6 @@ acrn_prepare_ovmf(struct vmctx *ctx) fclose(fp); printf("SW_LOAD: partition blob %s size %lu copy to guest 0x%lx\n", ovmf_path, ovmf_size, OVMF_TOP(ctx) - ovmf_size); - return 0; } @@ -171,3 +193,55 @@ acrn_sw_load_ovmf(struct vmctx *ctx) return 0; } + +/* The NV data section is the first 128KB in the OVMF image. At runtime, + * it's copied into guest memory and behave as RAM to OVMF. It can be + * accessed and updated by OVMF. To preserve NV section (referred to + * as Non-Volatile Data Store section in the OVMF spec), we're flushing + * in-memory data back to the NV data section of the OVMF image file + * at designated points. + */ +int +acrn_writeback_ovmf_nvstorage(struct vmctx *ctx) +{ + FILE *fp; + size_t write; + + if (!writeback_nv_storage) + return 0; + + fp = fopen(ovmf_path, "r+"); + if (fp == NULL) { + fprintf(stderr, + "OVMF_WRITEBACK ERR: could not open ovmf file: %s\n", + ovmf_path); + return -1; + } + + fseek(fp, 0, SEEK_END); + + if (ftell(fp) != ovmf_size) { + fprintf(stderr, + "SW_LOAD ERR: ovmf file changed\n"); + fclose(fp); + return -1; + } + + fseek(fp, 0, SEEK_SET); + write = fwrite(ctx->baseaddr + OVMF_NVSTORAGE_OFFSET, + sizeof(char), OVMF_NVSTORAGE_SZ, fp); + + if (write < OVMF_NVSTORAGE_SZ) { + fprintf(stderr, + "OVMF_WRITEBACK ERR: could not write back OVMF\n"); + fclose(fp); + return -1; + } + + fclose(fp); + printf("OVMF_WRITEBACK: OVMF has been written back \ + to partition blob %s size %lu from guest 0x%lx\n", + ovmf_path, OVMF_NVSTORAGE_SZ, OVMF_NVSTORAGE_OFFSET); + + return 0; +} diff --git a/devicemodel/include/sw_load.h b/devicemodel/include/sw_load.h index 74424edf8..71c2f0346 100644 --- a/devicemodel/include/sw_load.h +++ b/devicemodel/include/sw_load.h @@ -55,6 +55,7 @@ struct e820_entry { extern const struct e820_entry e820_default_entries[NUM_E820_ENTRIES]; extern int with_bootargs; +extern bool writeback_nv_storage; size_t ovmf_image_size(void); @@ -78,6 +79,7 @@ int acrn_sw_load_bzimage(struct vmctx *ctx); int acrn_sw_load_elf(struct vmctx *ctx); int acrn_sw_load_vsbl(struct vmctx *ctx); int acrn_sw_load_ovmf(struct vmctx *ctx); +int acrn_writeback_ovmf_nvstorage(struct vmctx *ctx); int acrn_sw_load(struct vmctx *ctx); #endif diff --git a/devicemodel/include/vmmapi.h b/devicemodel/include/vmmapi.h index f6c4598fd..64bb5d134 100644 --- a/devicemodel/include/vmmapi.h +++ b/devicemodel/include/vmmapi.h @@ -33,15 +33,13 @@ #include #include "types.h" #include "vmm.h" +#include "macros.h" /* * API version for out-of-tree consumers for making compile time decisions. */ #define VMMAPI_VERSION 0103 /* 2 digit major followed by 2 digit minor */ -#define MB (1024 * 1024UL) -#define GB (1024 * 1024 * 1024UL) - #define ALIGN_UP(x, align) (((x) + ((align)-1)) & ~((align)-1)) #define ALIGN_DOWN(x, align) ((x) & ~((align)-1))