summaryrefslogtreecommitdiffstats
path: root/usr.sbin
diff options
context:
space:
mode:
authorneel <neel@FreeBSD.org>2013-10-05 21:22:35 +0000
committerneel <neel@FreeBSD.org>2013-10-05 21:22:35 +0000
commitaed205d5cd3901acd2a0cf583e44ec166d99191a (patch)
tree7bf8725b3e8dc06e35d8c372329cbea900a7100c /usr.sbin
parent716c2031c71f1b1fbd78da6494f5c5e2a5115e9b (diff)
downloadFreeBSD-src-aed205d5cd3901acd2a0cf583e44ec166d99191a.zip
FreeBSD-src-aed205d5cd3901acd2a0cf583e44ec166d99191a.tar.gz
Merge projects/bhyve_npt_pmap into head.
Make the amd64/pmap code aware of nested page table mappings used by bhyve guests. This allows bhyve to associate each guest with its own vmspace and deal with nested page faults in the context of that vmspace. This also enables features like accessed/dirty bit tracking, swapping to disk and transparent superpage promotions of guest memory. Guest vmspace: Each bhyve guest has a unique vmspace to represent the physical memory allocated to the guest. Each memory segment allocated by the guest is mapped into the guest's address space via the 'vmspace->vm_map' and is backed by an object of type OBJT_DEFAULT. pmap types: The amd64/pmap now understands two types of pmaps: PT_X86 and PT_EPT. The PT_X86 pmap type is used by the vmspace associated with the host kernel as well as user processes executing on the host. The PT_EPT pmap is used by the vmspace associated with a bhyve guest. Page Table Entries: The EPT page table entries as mostly similar in functionality to regular page table entries although there are some differences in terms of what bits are used to express that functionality. For e.g. the dirty bit is represented by bit 9 in the nested PTE as opposed to bit 6 in the regular x86 PTE. Therefore the bitmask representing the dirty bit is now computed at runtime based on the type of the pmap. Thus PG_M that was previously a macro now becomes a local variable that is initialized at runtime using 'pmap_modified_bit(pmap)'. An additional wrinkle associated with EPT mappings is that older Intel processors don't have hardware support for tracking accessed/dirty bits in the PTE. This means that the amd64/pmap code needs to emulate these bits to provide proper accounting to the VM subsystem. This is achieved by using the following mapping for EPT entries that need emulation of A/D bits: Bit Position Interpreted By PG_V 52 software (accessed bit emulation handler) PG_RW 53 software (dirty bit emulation handler) PG_A 0 hardware (aka EPT_PG_RD) PG_M 1 hardware (aka EPT_PG_WR) The idea to use the mapping listed above for A/D bit emulation came from Alan Cox (alc@). The final difference with respect to x86 PTEs is that some EPT implementations do not support superpage mappings. This is recorded in the 'pm_flags' field of the pmap. TLB invalidation: The amd64/pmap code has a number of ways to do invalidation of mappings that may be cached in the TLB: single page, multiple pages in a range or the entire TLB. All of these funnel into a single EPT invalidation routine called 'pmap_invalidate_ept()'. This routine bumps up the EPT generation number and sends an IPI to the host cpus that are executing the guest's vcpus. On a subsequent entry into the guest it will detect that the EPT has changed and invalidate the mappings from the TLB. Guest memory access: Since the guest memory is no longer wired we need to hold the host physical page that backs the guest physical page before we can access it. The helper functions 'vm_gpa_hold()/vm_gpa_release()' are available for this purpose. PCI passthru: Guest's with PCI passthru devices will wire the entire guest physical address space. The MMIO BAR associated with the passthru device is backed by a vm_object of type OBJT_SG. An IOMMU domain is created only for guest's that have one or more PCI passthru devices attached to them. Limitations: There isn't a way to map a guest physical page without execute permissions. This is because the amd64/pmap code interprets the guest physical mappings as user mappings since they are numerically below VM_MAXUSER_ADDRESS. Since PG_U shares the same bit position as EPT_PG_EXECUTE all guest mappings become automatically executable. Thanks to Alan Cox and Konstantin Belousov for their rigorous code reviews as well as their support and encouragement. Thanks for John Baldwin for reviewing the use of OBJT_SG as the backing object for pci passthru mmio regions. Special thanks to Peter Holm for testing the patch on short notice. Approved by: re Discussed with: grehan Reviewed by: alc, kib Tested by: pho
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/bhyve/bhyverun.c28
-rw-r--r--usr.sbin/bhyve/pci_emul.c2
-rw-r--r--usr.sbin/bhyve/rtc.c4
-rw-r--r--usr.sbin/bhyvectl/bhyvectl.c39
-rw-r--r--usr.sbin/bhyveload/bhyveload.c4
5 files changed, 48 insertions, 29 deletions
diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c
index 5550f77..f37177f 100644
--- a/usr.sbin/bhyve/bhyverun.c
+++ b/usr.sbin/bhyve/bhyverun.c
@@ -101,7 +101,7 @@ struct bhyvestats {
uint64_t vmexit_hlt;
uint64_t vmexit_pause;
uint64_t vmexit_mtrap;
- uint64_t vmexit_paging;
+ uint64_t vmexit_inst_emul;
uint64_t cpu_switch_rotate;
uint64_t cpu_switch_direct;
int io_reset;
@@ -208,14 +208,12 @@ fbsdrun_addcpu(struct vmctx *ctx, int vcpu, uint64_t rip)
vmexit[vcpu].rip = rip;
vmexit[vcpu].inst_length = 0;
- if (vcpu == BSP) {
- mt_vmm_info[vcpu].mt_ctx = ctx;
- mt_vmm_info[vcpu].mt_vcpu = vcpu;
-
- error = pthread_create(&mt_vmm_info[vcpu].mt_thr, NULL,
- fbsdrun_start_thread, &mt_vmm_info[vcpu]);
- assert(error == 0);
- }
+ mt_vmm_info[vcpu].mt_ctx = ctx;
+ mt_vmm_info[vcpu].mt_vcpu = vcpu;
+
+ error = pthread_create(&mt_vmm_info[vcpu].mt_thr, NULL,
+ fbsdrun_start_thread, &mt_vmm_info[vcpu]);
+ assert(error == 0);
}
static int
@@ -385,13 +383,13 @@ vmexit_mtrap(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
}
static int
-vmexit_paging(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
+vmexit_inst_emul(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
{
int err;
- stats.vmexit_paging++;
+ stats.vmexit_inst_emul++;
- err = emulate_mem(ctx, *pvcpu, vmexit->u.paging.gpa,
- &vmexit->u.paging.vie);
+ err = emulate_mem(ctx, *pvcpu, vmexit->u.inst_emul.gpa,
+ &vmexit->u.inst_emul.vie);
if (err) {
if (err == EINVAL) {
@@ -400,7 +398,7 @@ vmexit_paging(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
vmexit->rip);
} else if (err == ESRCH) {
fprintf(stderr, "Unhandled memory access to 0x%lx\n",
- vmexit->u.paging.gpa);
+ vmexit->u.inst_emul.gpa);
}
return (VMEXIT_ABORT);
@@ -416,7 +414,7 @@ static vmexit_handler_t handler[VM_EXITCODE_MAX] = {
[VM_EXITCODE_RDMSR] = vmexit_rdmsr,
[VM_EXITCODE_WRMSR] = vmexit_wrmsr,
[VM_EXITCODE_MTRAP] = vmexit_mtrap,
- [VM_EXITCODE_PAGING] = vmexit_paging,
+ [VM_EXITCODE_INST_EMUL] = vmexit_inst_emul,
[VM_EXITCODE_SPINUP_AP] = vmexit_spinup_ap,
};
diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c
index 626dfdf..a95f261 100644
--- a/usr.sbin/bhyve/pci_emul.c
+++ b/usr.sbin/bhyve/pci_emul.c
@@ -1048,7 +1048,7 @@ init_pci(struct vmctx *ctx)
* Accesses to memory addresses that are not allocated to system
* memory or PCI devices return 0xff's.
*/
- error = vm_get_memory_seg(ctx, 0, &lowmem);
+ error = vm_get_memory_seg(ctx, 0, &lowmem, NULL);
assert(error == 0);
memset(&memp, 0, sizeof(struct mem_range));
diff --git a/usr.sbin/bhyve/rtc.c b/usr.sbin/bhyve/rtc.c
index e2b9f30..c1a84d74 100644
--- a/usr.sbin/bhyve/rtc.c
+++ b/usr.sbin/bhyve/rtc.c
@@ -341,14 +341,14 @@ rtc_init(struct vmctx *ctx)
* 0x34/0x35 - 64KB chunks above 16MB, below 4GB
* 0x5b/0x5c/0x5d - 64KB chunks above 4GB
*/
- err = vm_get_memory_seg(ctx, 0, &lomem);
+ err = vm_get_memory_seg(ctx, 0, &lomem, NULL);
assert(err == 0);
lomem = (lomem - m_16MB) / m_64KB;
rtc_nvram[nvoff(RTC_LMEM_LSB)] = lomem;
rtc_nvram[nvoff(RTC_LMEM_MSB)] = lomem >> 8;
- if (vm_get_memory_seg(ctx, m_4GB, &himem) == 0) {
+ if (vm_get_memory_seg(ctx, m_4GB, &himem, NULL) == 0) {
himem /= m_64KB;
rtc_nvram[nvoff(RTC_HMEM_LSB)] = himem;
rtc_nvram[nvoff(RTC_HMEM_SB)] = himem >> 8;
diff --git a/usr.sbin/bhyvectl/bhyvectl.c b/usr.sbin/bhyvectl/bhyvectl.c
index 438d01c..d6b32b8 100644
--- a/usr.sbin/bhyvectl/bhyvectl.c
+++ b/usr.sbin/bhyvectl/bhyvectl.c
@@ -188,12 +188,13 @@ usage(void)
" [--unassign-pptdev=<bus/slot/func>]\n"
" [--set-mem=<memory in units of MB>]\n"
" [--get-lowmem]\n"
- " [--get-highmem]\n",
+ " [--get-highmem]\n"
+ " [--get-gpa-pmap]\n",
progname);
exit(1);
}
-static int get_stats, getcap, setcap, capval;
+static int get_stats, getcap, setcap, capval, get_gpa_pmap;
static const char *capname;
static int create, destroy, get_lowmem, get_highmem;
static uint64_t memsize;
@@ -377,18 +378,20 @@ enum {
SET_CAP,
CAPNAME,
UNASSIGN_PPTDEV,
+ GET_GPA_PMAP,
};
int
main(int argc, char *argv[])
{
char *vmname;
- int error, ch, vcpu;
- vm_paddr_t gpa;
+ int error, ch, vcpu, ptenum;
+ vm_paddr_t gpa, gpa_pmap;
size_t len;
struct vm_exit vmexit;
- uint64_t ctl, eptp, bm, addr, u64;
+ uint64_t ctl, eptp, bm, addr, u64, pteval[4], *pte;
struct vmctx *ctx;
+ int wired;
uint64_t cr0, cr3, cr4, dr7, rsp, rip, rflags, efer, pat;
uint64_t rax, rbx, rcx, rdx, rsi, rdi, rbp;
@@ -427,6 +430,7 @@ main(int argc, char *argv[])
{ "capname", REQ_ARG, 0, CAPNAME },
{ "unassign-pptdev", REQ_ARG, 0, UNASSIGN_PPTDEV },
{ "setcap", REQ_ARG, 0, SET_CAP },
+ { "get-gpa-pmap", REQ_ARG, 0, GET_GPA_PMAP },
{ "getcap", NO_ARG, &getcap, 1 },
{ "get-stats", NO_ARG, &get_stats, 1 },
{ "get-desc-ds",NO_ARG, &get_desc_ds, 1 },
@@ -666,6 +670,10 @@ main(int argc, char *argv[])
capval = strtoul(optarg, NULL, 0);
setcap = 1;
break;
+ case GET_GPA_PMAP:
+ gpa_pmap = strtoul(optarg, NULL, 0);
+ get_gpa_pmap = 1;
+ break;
case CAPNAME:
capname = optarg;
break;
@@ -819,16 +827,18 @@ main(int argc, char *argv[])
if (!error && (get_lowmem || get_all)) {
gpa = 0;
- error = vm_get_memory_seg(ctx, gpa, &len);
+ error = vm_get_memory_seg(ctx, gpa, &len, &wired);
if (error == 0)
- printf("lowmem\t\t0x%016lx/%ld\n", gpa, len);
+ printf("lowmem\t\t0x%016lx/%ld%s\n", gpa, len,
+ wired ? " wired" : "");
}
if (!error && (get_highmem || get_all)) {
gpa = 4 * GB;
- error = vm_get_memory_seg(ctx, gpa, &len);
+ error = vm_get_memory_seg(ctx, gpa, &len, &wired);
if (error == 0)
- printf("highmem\t\t0x%016lx/%ld\n", gpa, len);
+ printf("highmem\t\t0x%016lx/%ld%s\n", gpa, len,
+ wired ? " wired" : "");
}
if (!error && (get_efer || get_all)) {
@@ -1457,6 +1467,17 @@ main(int argc, char *argv[])
printf("Capability \"%s\" is not available\n", capname);
}
+ if (!error && get_gpa_pmap) {
+ error = vm_get_gpa_pmap(ctx, gpa_pmap, pteval, &ptenum);
+ if (error == 0) {
+ printf("gpa %#lx:", gpa_pmap);
+ pte = &pteval[0];
+ while (ptenum-- > 0)
+ printf(" %#lx", *pte++);
+ printf("\n");
+ }
+ }
+
if (!error && (getcap || get_all)) {
int captype, val, getcaptype;
diff --git a/usr.sbin/bhyveload/bhyveload.c b/usr.sbin/bhyveload/bhyveload.c
index 4cd280c..c4bafd3 100644
--- a/usr.sbin/bhyveload/bhyveload.c
+++ b/usr.sbin/bhyveload/bhyveload.c
@@ -492,8 +492,8 @@ static void
cb_getmem(void *arg, uint64_t *ret_lowmem, uint64_t *ret_highmem)
{
- vm_get_memory_seg(ctx, 0, ret_lowmem);
- vm_get_memory_seg(ctx, 4 * GB, ret_highmem);
+ vm_get_memory_seg(ctx, 0, ret_lowmem, NULL);
+ vm_get_memory_seg(ctx, 4 * GB, ret_highmem, NULL);
}
static const char *
OpenPOWER on IntegriCloud