genode/repos/ports/src/virtualbox5/spec/nova/pgm.cc

261 lines
9.1 KiB
C++

/*
* \brief Genode/Nova specific PGM code to resolve EPT faults
* \author Alexander Boettcher
*/
/*
* Copyright (C) 2016-2017 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include "vcpu.h"
#include "PGMInline.h"
enum { VERBOSE_PGM = false };
int Vcpu_handler::map_memory(RTGCPHYS GCPhys, size_t cbWrite,
RTGCUINT vbox_fault_reason,
Genode::Flexpage_iterator &fli, bool &writeable)
{
using Genode::Flexpage_iterator;
using Genode::addr_t;
PVM pVM = _current_vm;
PVMCPU pVCpu = _current_vcpu;
_ept_fault_addr_type = PGMPAGETYPE_INVALID;
/* DON'T USE normal printf in this function - corrupts unsaved UTCB !!! */
PPGMRAMRANGE pRam = pgmPhysGetRangeAtOrAbove(pVM, GCPhys);
if (!pRam)
return VERR_PGM_DYNMAP_FAILED;
/* XXX pgmLock/pgmUnlock XXX */
RTGCPHYS off = GCPhys - pRam->GCPhys;
if (off >= pRam->cb)
return VERR_PGM_DYNMAP_FAILED;
unsigned iPage = off >> PAGE_SHIFT;
PPGMPAGE pPage = &pRam->aPages[iPage];
_ept_fault_addr_type = PGM_PAGE_GET_TYPE(pPage);
/*
* If page is not allocated (== zero page) and no MMIO or active page, allocate and map it
* immediately. Important do not do this if A20 gate is disabled, A20 gate
* is handled by IEM/REM in this case.
*/
if (PGM_PAGE_IS_ZERO(pPage)
&& !PGM_PAGE_IS_ALLOCATED(pPage)
&& !PGM_PAGE_HAS_ACTIVE_ALL_HANDLERS(pPage)
&& !PGM_PAGE_IS_SPECIAL_ALIAS_MMIO(pPage)
&& PGM_A20_IS_ENABLED(pVCpu)) {
pgmLock(pVM);
pgmPhysPageMakeWritable(pVM, pPage, GCPhys);
pgmUnlock(pVM);
}
if (PGM_PAGE_HAS_ACTIVE_ALL_HANDLERS(pPage) ||
PGM_PAGE_IS_SPECIAL_ALIAS_MMIO(pPage) ||
PGM_PAGE_IS_ZERO(pPage)) {
if (PGM_PAGE_GET_TYPE(pPage) != PGMPAGETYPE_MMIO &&
!PGM_PAGE_IS_ZERO(pPage)) {
Vmm::log(__LINE__, " GCPhys=", Genode::Hex(GCPhys), " ",
PGM_PAGE_HAS_ACTIVE_ALL_HANDLERS(pPage), " ",
PGM_PAGE_IS_SPECIAL_ALIAS_MMIO(pPage), " ",
PGM_PAGE_IS_ZERO(pPage), " "
" vbox_fault_reason=", Genode::Hex(vbox_fault_reason));
Vmm::log(__LINE__, " GCPhys=", Genode::Hex(GCPhys), " "
"host=", Genode::Hex(PGM_PAGE_GET_HCPHYS(pPage)), " "
"type=", Genode::Hex(PGM_PAGE_GET_TYPE(pPage)), " "
"writeable=", writeable, " "
"state=", Genode::Hex(PGM_PAGE_GET_STATE(pPage)));
}
return VERR_PGM_DYNMAP_FAILED;
}
if (!PGM_PAGE_IS_ALLOCATED(pPage))
Vmm::log("unknown page state ", Genode::Hex(PGM_PAGE_GET_STATE(pPage)),
" GCPhys=", Genode::Hex(GCPhys));
Assert(PGM_PAGE_IS_ALLOCATED(pPage));
if (PGM_PAGE_GET_TYPE(pPage) != PGMPAGETYPE_RAM &&
PGM_PAGE_GET_TYPE(pPage) != PGMPAGETYPE_MMIO2 &&
PGM_PAGE_GET_TYPE(pPage) != PGMPAGETYPE_ROM)
{
if (VERBOSE_PGM)
Vmm::log(__LINE__, " GCPhys=", Genode::Hex(GCPhys), " "
"vbox_fault_reason=", Genode::Hex(vbox_fault_reason), " "
"host=", Genode::Hex(PGM_PAGE_GET_HCPHYS(pPage)), " "
"type=", Genode::Hex(PGM_PAGE_GET_TYPE(pPage)), " "
"state=", Genode::Hex(PGM_PAGE_GET_STATE(pPage)));
return VERR_PGM_DYNMAP_FAILED;
}
Assert(!PGM_PAGE_IS_ZERO(pPage));
/* write fault on a ROM region */
if (PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_ROM &&
vbox_fault_reason & VMX_EXIT_QUALIFICATION_EPT_DATA_WRITE) {
Vmm::warning(__func__, " - write fault on ROM region!? gp=",
Genode::Hex(GCPhys));
return VERR_PGM_DYNMAP_FAILED;
}
/* nothing should be mapped - otherwise we get endless overmap loops */
Assert(!(vbox_fault_reason & VMX_EXIT_QUALIFICATION_EPT_ENTRY_PRESENT));
writeable = PGM_PAGE_GET_TYPE(pPage) != PGMPAGETYPE_ROM;
PPGMPHYSHANDLER handler = pgmHandlerPhysicalLookup(pVM, GCPhys);
if (VERBOSE_PGM && PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_MMIO2 &&
!handler)
Vmm::log(__LINE__, " GCPhys=", Genode::Hex(GCPhys), " ",
"type=", Genode::Hex(PGM_PAGE_GET_TYPE(pPage)), " "
"state=", Genode::Hex(PGM_PAGE_GET_STATE(pPage)), " "
"- MMIO2 w/o handler");
if (PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_MMIO2 && handler) {
PFNPGMPHYSHANDLER pfnHandler = PGMPHYSHANDLER_GET_TYPE(pVM, handler)->CTX_SUFF(pfnHandler);
if (!pfnHandler) {
Vmm::log(__LINE__, " GCPhys=", Genode::Hex(GCPhys), " "
"type=", Genode::Hex(PGM_PAGE_GET_TYPE(pPage)));
return VERR_PGM_DYNMAP_FAILED;
}
void *pvUser = handler->CTX_SUFF(pvUser);
if (!pvUser) {
Vmm::log(__LINE__, " GCPhys=", Genode::Hex(GCPhys), " "
"type=", Genode::Hex(PGM_PAGE_GET_TYPE(pPage)));
return VERR_PGM_DYNMAP_FAILED;
}
PGMACCESSTYPE access_type = (vbox_fault_reason & VMX_EXIT_QUALIFICATION_EPT_DATA_WRITE) ? PGMACCESSTYPE_WRITE : PGMACCESSTYPE_READ;
VBOXSTRICTRC rcStrict = pfnHandler(pVM, pVCpu, GCPhys, nullptr, nullptr, 0, access_type, PGMACCESSORIGIN_HM, pvUser);
if (rcStrict != VINF_PGM_HANDLER_DO_DEFAULT) {
Vmm::log(__LINE__, " nodefault GCPhys=", Genode::Hex(GCPhys), " "
"type=", Genode::Hex(PGM_PAGE_GET_TYPE(pPage)), " "
"pfnHandler=", pfnHandler);
return VERR_PGM_DYNMAP_FAILED;
}
}
/*
if (VERBOSE_PGM)
Vmm::log(Genode::Hex(PGM_PAGE_GET_HCPHYS(pPage)),
"->", Genode::Hex(GCPhys),
" type=", PGM_PAGE_GET_TYPE(pPage),
" state=", PGM_PAGE_GET_STATE(pPage),
" pde_type=", PGM_PAGE_GET_PDE_TYPE(pPage),
PGM_PAGE_GET_PDE_TYPE(pPage) == PGM_PAGE_PDE_TYPE_PDE ? "(is pde)" : "(not pde)",
" iPage=", iPage,
" range_start=", Genode::Hex(pRam->GCPhys),
" range_size=", Genode::Hex(pRam->cb),
" pages=", pRam->cb >> PAGE_SHIFT
);
*/
/* setup mapping for just a page as standard */
fli = Flexpage_iterator(PGM_PAGE_GET_HCPHYS(pPage), 4096,
GCPhys & ~0xFFFUL, 4096, GCPhys & ~0xFFFUL);
if (PGM_PAGE_GET_PDE_TYPE(pPage) != PGM_PAGE_PDE_TYPE_PDE)
return VINF_SUCCESS; /* one page mapping */
Genode::addr_t const superpage_log2 = 21;
Genode::addr_t const max_pages = pRam->cb >> PAGE_SHIFT;
Genode::addr_t const superpage_pages = (1UL << superpage_log2) / 4096;
Genode::addr_t const mask = (1UL << superpage_log2) - 1;
Genode::addr_t const super_gcphys = GCPhys & ~mask;
RTGCPHYS max_off = super_gcphys - pRam->GCPhys;
if (max_off > pRam->cb)
return VINF_SUCCESS;
Genode::addr_t super_hcphys = PGM_PAGE_GET_HCPHYS(pPage) & ~mask;
unsigned const i_s = max_off >> PAGE_SHIFT;
if (i_s + superpage_pages > max_pages)
return VINF_SUCCESS; /* one page mapping */
if (VERBOSE_PGM)
Vmm::log(Genode::Hex(PGM_PAGE_GET_HCPHYS(pPage)), "->",
Genode::Hex(GCPhys), " - iPage ", iPage, " [",
i_s, ",", i_s + superpage_pages, ")", " "
"range_size=", Genode::Hex(pRam->cb));
/* paranoia sanity checks */
for (Genode::addr_t i = i_s; i < i_s + superpage_pages; i++) {
PPGMPAGE page = &pRam->aPages[i];
Genode::addr_t const gcpage = pRam->GCPhys + (i << PAGE_SHIFT);
if (!(super_hcphys == (PGM_PAGE_GET_HCPHYS(page) & ~mask)) ||
!(super_gcphys == (gcpage & ~mask)) ||
!(PGM_PAGE_GET_PDE_TYPE(page) == PGM_PAGE_PDE_TYPE_PDE) ||
!(PGM_PAGE_GET_TYPE(page) == PGM_PAGE_GET_TYPE(pPage)) ||
!(PGM_PAGE_GET_STATE(page) == PGM_PAGE_GET_STATE(pPage)))
{
if (VERBOSE_PGM)
Vmm::error(Genode::Hex(PGM_PAGE_GET_HCPHYS(pPage)), "->",
Genode::Hex(GCPhys), " - iPage ", iPage, " i ", i, " [",
i_s, ",", i_s + superpage_pages, ")", " "
"range_size=", Genode::Hex(pRam->cb), " "
"super_hcphys=", Genode::Hex(super_hcphys), "?=", Genode::Hex((PGM_PAGE_GET_HCPHYS(page) & ~mask)), " "
"super_gcphys=", Genode::Hex(super_gcphys), "?=", Genode::Hex((gcpage & ~mask)), " ",
(int)(PGM_PAGE_GET_PDE_TYPE(page)), "?=", (int)PGM_PAGE_PDE_TYPE_PDE, " ",
(int)(PGM_PAGE_GET_TYPE(page)), "?=", (int)PGM_PAGE_GET_TYPE(pPage), " ",
(int)(PGM_PAGE_GET_STATE(page)), "?=", PGM_PAGE_GET_STATE(pPage));
return VINF_SUCCESS; /* one page mapping */
}
}
/* overwrite one-page mapping with super page mapping */
fli = Flexpage_iterator(super_hcphys, 1 << superpage_log2,
super_gcphys, 1 << superpage_log2, super_gcphys);
/* revoke old mappings, e.g. less permissions or small pages */
Nova::Rights const revoke_rwx(true, true, true);
Nova::Crd crd = Nova::Mem_crd(super_hcphys >> 12, superpage_log2 - 12, revoke_rwx);
Nova::revoke(crd, false);
return VINF_SUCCESS; /* super page mapping */
}
Genode::uint64_t * Vcpu_handler::pdpte_map(VM *pVM, RTGCPHYS cr3)
{
PPGMRAMRANGE pRam = pgmPhysGetRangeAtOrAbove(pVM, cr3);
Assert (pRam);
/* XXX pgmLock/pgmUnlock XXX */
RTGCPHYS off = cr3 - pRam->GCPhys;
Assert (off < pRam->cb);
unsigned iPage = off >> PAGE_SHIFT;
PPGMPAGE pPage = &pRam->aPages[iPage];
/*
if (VERBOSE_PGM)
Vmm::log(__LINE__, " gcphys=", Vmm::Hex(cr3),
" host=", Vmm::Hex(PGM_PAGE_GET_HCPHYS(pPage)),
" type=", Vmm::Hex(PGM_PAGE_GET_TYPE(pPage)),
" state=",Vmm::Hex(PGM_PAGE_GET_STATE(pPage)));
*/
Genode::uint64_t *pdpte = reinterpret_cast<Genode::uint64_t*>(PGM_PAGE_GET_HCPHYS(pPage) + (cr3 & PAGE_OFFSET_MASK));
Assert(pdpte != 0);
return pdpte;
}