2018-12-12 17:46:16 +08:00
|
|
|
/*-
|
|
|
|
* Copyright (c) 2018 Intel Corporation
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include "dm.h"
|
|
|
|
#include "vmmapi.h"
|
|
|
|
#include "sw_load.h"
|
|
|
|
|
|
|
|
|
|
|
|
/* ovmf binary layout:
|
|
|
|
*
|
|
|
|
* +--------------------------------------------------+ <--OVMF Top
|
|
|
|
* | |offset: Top - 0x10 (reset vector) |
|
|
|
|
* + SECFV |------------------------------------+
|
|
|
|
* | |other |
|
|
|
|
* +--------------------------------------------------+
|
|
|
|
* | |
|
|
|
|
* + FVMAIN_COMPACT +
|
|
|
|
* | |
|
|
|
|
* +--------------------------------------------------+
|
|
|
|
* | |
|
|
|
|
* + NV data storage +
|
|
|
|
* | |
|
2019-07-12 06:53:03 +08:00
|
|
|
* +--------------------------------------------------+ <--OVMF offset 0
|
2018-12-12 17:46:16 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* ovmf real entry is reset vector, which is (OVMF_TOP - 16) */
|
dm: build E820 map for OVMF
OVMF requires a more descriptive mechanism than RTC CMOS to retrieve
ACRN's memory layout, so we now pass the E820 map to it, starting at
0xEF000 (ROM area).
ACRN currently uses [4GB, 5GB) as its 64-bit PCI host aperture. This is
inconsistent with OVMF's assumption of its platform's memory layout,
because it derives the size of high memory from RTC CMOS, which is
incapable of describing the 64-bit PCI hole.
By default, OVMF uses RTC CMOS 0x5b/0x5c/0x5d to determine the size of
high memory. This value only tells OVMF how much memory is above 4GB,
but not the platform's memory layout above 4GB.
Using RTC CMOS works for QEMU, because QEMU places its 64-bit PCI host
aperture above its highmem. Therefore, OVMF can always assume highmem is
located at [4GB, 4GB + highmem), which is not where ACRN's highmem is
located. For example, if we have 1GB of usable memory above 4GB, ACRN
will place it at [5GB, 6GB).
This change allows OVMF to correctly identify the guest's memory layout.
It will consider any reserved region above 4GB as 64-bit PCI host
aperture.
MP table, SMBIOS and ACPI tables are all located above 0xF0000 so it is
guaranteed that there is no overlap. There can only be a maximum of 128
E820 entries.
v1 -> v2:
- provide more explanation to this commit
- add signature before E820 map for OVMF backward compatibility
Tracked-On: #2792
Signed-off-by: Peter Fang <peter.fang@intel.com>
Reviewed-by: Eddie Dong <eddie.dong@intel.com>
Acked-by: Yin Fengwei <fengwei.yin@intel.com>
2019-03-15 17:31:33 +08:00
|
|
|
#define OVMF_TOP(ctx) (4*GB)
|
|
|
|
|
2019-07-12 06:53:03 +08:00
|
|
|
/* 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)
|
|
|
|
|
dm: build E820 map for OVMF
OVMF requires a more descriptive mechanism than RTC CMOS to retrieve
ACRN's memory layout, so we now pass the E820 map to it, starting at
0xEF000 (ROM area).
ACRN currently uses [4GB, 5GB) as its 64-bit PCI host aperture. This is
inconsistent with OVMF's assumption of its platform's memory layout,
because it derives the size of high memory from RTC CMOS, which is
incapable of describing the 64-bit PCI hole.
By default, OVMF uses RTC CMOS 0x5b/0x5c/0x5d to determine the size of
high memory. This value only tells OVMF how much memory is above 4GB,
but not the platform's memory layout above 4GB.
Using RTC CMOS works for QEMU, because QEMU places its 64-bit PCI host
aperture above its highmem. Therefore, OVMF can always assume highmem is
located at [4GB, 4GB + highmem), which is not where ACRN's highmem is
located. For example, if we have 1GB of usable memory above 4GB, ACRN
will place it at [5GB, 6GB).
This change allows OVMF to correctly identify the guest's memory layout.
It will consider any reserved region above 4GB as 64-bit PCI host
aperture.
MP table, SMBIOS and ACPI tables are all located above 0xF0000 so it is
guaranteed that there is no overlap. There can only be a maximum of 128
E820 entries.
v1 -> v2:
- provide more explanation to this commit
- add signature before E820 map for OVMF backward compatibility
Tracked-On: #2792
Signed-off-by: Peter Fang <peter.fang@intel.com>
Reviewed-by: Eddie Dong <eddie.dong@intel.com>
Acked-by: Yin Fengwei <fengwei.yin@intel.com>
2019-03-15 17:31:33 +08:00
|
|
|
/* located in the ROM area */
|
|
|
|
#define OVMF_E820_BASE 0x000EF000UL
|
2018-12-12 17:46:16 +08:00
|
|
|
|
|
|
|
static char ovmf_path[STR_LEN];
|
|
|
|
static size_t ovmf_size;
|
2019-07-12 06:53:03 +08:00
|
|
|
bool writeback_nv_storage;
|
2018-12-12 17:46:16 +08:00
|
|
|
|
|
|
|
extern int init_cmos_vrpmb(struct vmctx *ctx);
|
|
|
|
|
|
|
|
size_t
|
|
|
|
ovmf_image_size(void)
|
|
|
|
{
|
|
|
|
return ovmf_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
acrn_parse_ovmf(char *arg)
|
|
|
|
{
|
2019-06-17 18:10:14 +08:00
|
|
|
int error = -1;
|
2019-07-12 06:53:03 +08:00
|
|
|
char *str, *cp, *token = NULL;
|
2018-12-25 10:33:36 +08:00
|
|
|
size_t len = strnlen(arg, STR_LEN);
|
2018-12-12 17:46:16 +08:00
|
|
|
|
2019-07-12 06:53:03 +08:00
|
|
|
str = strdup(arg);
|
2018-12-12 17:46:16 +08:00
|
|
|
if (len < STR_LEN) {
|
2019-07-12 06:53:03 +08:00
|
|
|
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, ",");
|
2019-06-17 18:10:14 +08:00
|
|
|
}
|
|
|
|
}
|
2019-07-12 06:53:03 +08:00
|
|
|
free(str);
|
2019-06-17 18:10:14 +08:00
|
|
|
return error;
|
2018-12-12 17:46:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
acrn_prepare_ovmf(struct vmctx *ctx)
|
|
|
|
{
|
|
|
|
FILE *fp;
|
|
|
|
size_t read;
|
|
|
|
|
|
|
|
fp = fopen(ovmf_path, "r");
|
|
|
|
if (fp == NULL) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"SW_LOAD 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);
|
|
|
|
read = fread(ctx->baseaddr + OVMF_TOP(ctx) - ovmf_size,
|
|
|
|
sizeof(char), ovmf_size, fp);
|
|
|
|
|
|
|
|
if (read < ovmf_size) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"SW_LOAD ERR: could not read whole partition blob\n");
|
|
|
|
fclose(fp);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
acrn_sw_load_ovmf(struct vmctx *ctx)
|
|
|
|
{
|
|
|
|
int ret;
|
dm: build E820 map for OVMF
OVMF requires a more descriptive mechanism than RTC CMOS to retrieve
ACRN's memory layout, so we now pass the E820 map to it, starting at
0xEF000 (ROM area).
ACRN currently uses [4GB, 5GB) as its 64-bit PCI host aperture. This is
inconsistent with OVMF's assumption of its platform's memory layout,
because it derives the size of high memory from RTC CMOS, which is
incapable of describing the 64-bit PCI hole.
By default, OVMF uses RTC CMOS 0x5b/0x5c/0x5d to determine the size of
high memory. This value only tells OVMF how much memory is above 4GB,
but not the platform's memory layout above 4GB.
Using RTC CMOS works for QEMU, because QEMU places its 64-bit PCI host
aperture above its highmem. Therefore, OVMF can always assume highmem is
located at [4GB, 4GB + highmem), which is not where ACRN's highmem is
located. For example, if we have 1GB of usable memory above 4GB, ACRN
will place it at [5GB, 6GB).
This change allows OVMF to correctly identify the guest's memory layout.
It will consider any reserved region above 4GB as 64-bit PCI host
aperture.
MP table, SMBIOS and ACPI tables are all located above 0xF0000 so it is
guaranteed that there is no overlap. There can only be a maximum of 128
E820 entries.
v1 -> v2:
- provide more explanation to this commit
- add signature before E820 map for OVMF backward compatibility
Tracked-On: #2792
Signed-off-by: Peter Fang <peter.fang@intel.com>
Reviewed-by: Eddie Dong <eddie.dong@intel.com>
Acked-by: Yin Fengwei <fengwei.yin@intel.com>
2019-03-15 17:31:33 +08:00
|
|
|
struct {
|
|
|
|
char signature[4];
|
|
|
|
uint32_t nentries;
|
|
|
|
struct e820_entry map[];
|
|
|
|
} __attribute__((packed)) *e820;
|
2018-12-12 17:46:16 +08:00
|
|
|
|
|
|
|
init_cmos_vrpmb(ctx);
|
|
|
|
|
|
|
|
ret = acrn_prepare_ovmf(ctx);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
dm: build E820 map for OVMF
OVMF requires a more descriptive mechanism than RTC CMOS to retrieve
ACRN's memory layout, so we now pass the E820 map to it, starting at
0xEF000 (ROM area).
ACRN currently uses [4GB, 5GB) as its 64-bit PCI host aperture. This is
inconsistent with OVMF's assumption of its platform's memory layout,
because it derives the size of high memory from RTC CMOS, which is
incapable of describing the 64-bit PCI hole.
By default, OVMF uses RTC CMOS 0x5b/0x5c/0x5d to determine the size of
high memory. This value only tells OVMF how much memory is above 4GB,
but not the platform's memory layout above 4GB.
Using RTC CMOS works for QEMU, because QEMU places its 64-bit PCI host
aperture above its highmem. Therefore, OVMF can always assume highmem is
located at [4GB, 4GB + highmem), which is not where ACRN's highmem is
located. For example, if we have 1GB of usable memory above 4GB, ACRN
will place it at [5GB, 6GB).
This change allows OVMF to correctly identify the guest's memory layout.
It will consider any reserved region above 4GB as 64-bit PCI host
aperture.
MP table, SMBIOS and ACPI tables are all located above 0xF0000 so it is
guaranteed that there is no overlap. There can only be a maximum of 128
E820 entries.
v1 -> v2:
- provide more explanation to this commit
- add signature before E820 map for OVMF backward compatibility
Tracked-On: #2792
Signed-off-by: Peter Fang <peter.fang@intel.com>
Reviewed-by: Eddie Dong <eddie.dong@intel.com>
Acked-by: Yin Fengwei <fengwei.yin@intel.com>
2019-03-15 17:31:33 +08:00
|
|
|
e820 = paddr_guest2host(ctx, OVMF_E820_BASE,
|
|
|
|
e820_default_entries[LOWRAM_E820_ENTRY].baseaddr -
|
|
|
|
OVMF_E820_BASE);
|
2019-06-17 18:10:14 +08:00
|
|
|
if (e820 == NULL)
|
|
|
|
return -1;
|
dm: build E820 map for OVMF
OVMF requires a more descriptive mechanism than RTC CMOS to retrieve
ACRN's memory layout, so we now pass the E820 map to it, starting at
0xEF000 (ROM area).
ACRN currently uses [4GB, 5GB) as its 64-bit PCI host aperture. This is
inconsistent with OVMF's assumption of its platform's memory layout,
because it derives the size of high memory from RTC CMOS, which is
incapable of describing the 64-bit PCI hole.
By default, OVMF uses RTC CMOS 0x5b/0x5c/0x5d to determine the size of
high memory. This value only tells OVMF how much memory is above 4GB,
but not the platform's memory layout above 4GB.
Using RTC CMOS works for QEMU, because QEMU places its 64-bit PCI host
aperture above its highmem. Therefore, OVMF can always assume highmem is
located at [4GB, 4GB + highmem), which is not where ACRN's highmem is
located. For example, if we have 1GB of usable memory above 4GB, ACRN
will place it at [5GB, 6GB).
This change allows OVMF to correctly identify the guest's memory layout.
It will consider any reserved region above 4GB as 64-bit PCI host
aperture.
MP table, SMBIOS and ACPI tables are all located above 0xF0000 so it is
guaranteed that there is no overlap. There can only be a maximum of 128
E820 entries.
v1 -> v2:
- provide more explanation to this commit
- add signature before E820 map for OVMF backward compatibility
Tracked-On: #2792
Signed-off-by: Peter Fang <peter.fang@intel.com>
Reviewed-by: Eddie Dong <eddie.dong@intel.com>
Acked-by: Yin Fengwei <fengwei.yin@intel.com>
2019-03-15 17:31:33 +08:00
|
|
|
|
|
|
|
strncpy(e820->signature, "820", sizeof(e820->signature));
|
|
|
|
e820->nentries = acrn_create_e820_table(ctx, e820->map);
|
|
|
|
|
2018-12-12 17:46:16 +08:00
|
|
|
printf("SW_LOAD: ovmf_entry 0x%lx\n", OVMF_TOP(ctx) - 16);
|
|
|
|
|
|
|
|
/* set guest bsp state. Will call hypercall set bsp state
|
|
|
|
* after bsp is created.
|
|
|
|
*/
|
|
|
|
memset(&ctx->bsp_regs, 0, sizeof(struct acrn_set_vcpu_regs));
|
|
|
|
ctx->bsp_regs.vcpu_id = 0;
|
|
|
|
|
|
|
|
/* CR0_ET | CR0_NE */
|
|
|
|
ctx->bsp_regs.vcpu_regs.cr0 = 0x30U;
|
|
|
|
ctx->bsp_regs.vcpu_regs.cs_ar = 0x009FU;
|
|
|
|
ctx->bsp_regs.vcpu_regs.cs_sel = 0xF000U;
|
|
|
|
ctx->bsp_regs.vcpu_regs.cs_limit = 0xFFFFU;
|
|
|
|
ctx->bsp_regs.vcpu_regs.cs_base = (OVMF_TOP(ctx) - 16) & 0xFFFF0000UL;
|
|
|
|
ctx->bsp_regs.vcpu_regs.rip = (OVMF_TOP(ctx) - 16) & 0xFFFFUL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-07-12 06:53:03 +08:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|