genode/repos/ports/src/virtualbox/pgm.cc
Norman Feske 4d442bca30 Streamline exception types
This patch reduces the number of exception types by facilitating
globally defined exceptions for common usage patterns shared by most
services. In particular, RPC functions that demand a session-resource
upgrade not longer reflect this condition via a session-specific
exception but via the 'Out_of_ram' or 'Out_of_caps' types.

Furthermore, the 'Parent::Service_denied', 'Parent::Unavailable',
'Root::Invalid_args', 'Root::Unavailable', 'Service::Invalid_args',
'Service::Unavailable', and 'Local_service::Factory::Denied' types have
been replaced by the single 'Service_denied' exception type defined in
'session/session.h'.

This consolidation eases the error handling (there are fewer exceptions
to handle), alleviates the need to convert exceptions along the
session-creation call chain, and avoids possible aliasing problems
(catching the wrong type with the same name but living in a different
scope).
2017-05-31 13:16:07 +02:00

673 lines
16 KiB
C++

/*
* \brief VirtualBox page manager (PGM)
* \author Norman Feske
* \date 2013-08-20
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
/* Genode includes */
#include <base/log.h>
#include <util/string.h>
/* VirtualBox includes */
#include "PGMInternal.h" /* enable access to pgm.s.* */
#include "EMInternal.h"
#include <VBox/vmm/mm.h>
#include <VBox/vmm/vm.h>
#include <VBox/vmm/pgm.h>
#include <VBox/vmm/rem.h>
#include <iprt/err.h>
/* local includes */
#include "util.h"
#include "vmm_memory.h"
#include "guest_memory.h"
using Genode::Ram_session;
using Genode::Rm_session;
Vmm_memory *vmm_memory()
{
static Vmm_memory inst(genode_env());
return &inst;
}
Guest_memory *guest_memory()
{
static Guest_memory inst;
return &inst;
}
static DECLCALLBACK(int) romwritehandler(PVM pVM, RTGCPHYS GCPhys,
void *pvPhys, void *pvBuf,
size_t cbBuf,
PGMACCESSTYPE enmAccessType,
void *pvUser)
{
Assert(!"Somebody tries to write to ROM");
return -1;
}
int PGMR3PhysRomRegister(PVM pVM, PPDMDEVINS pDevIns, RTGCPHYS GCPhys,
RTGCPHYS cb, const void *pvBinary, uint32_t cbBinary,
uint32_t fFlags, const char *pszDesc)
{
try {
RTGCPHYS GCPhysLast = GCPhys + (cb - 1);
size_t size = (size_t)cb;
Assert(cb == size);
void *pv = vmm_memory()->alloc_rom(size, pDevIns);
Assert(pv);
memcpy(pv, pvBinary, size);
/* associate memory of VMM with guest VM */
vmm_memory()->map_to_vm(pDevIns, GCPhys);
guest_memory()->add_rom_mapping(GCPhys, cb, pv, pDevIns);
bool fShadowed = fFlags & PGMPHYS_ROM_FLAGS_SHADOWED;
Assert(!fShadowed);
int rc = PGMR3HandlerPhysicalRegister(pVM,
PGMPHYSHANDLERTYPE_PHYSICAL_WRITE,
GCPhys, GCPhysLast,
romwritehandler,
NULL,
NULL, NULL, 0,
NULL, NULL, 0, pszDesc);
Assert(rc == VINF_SUCCESS);
#ifdef VBOX_WITH_REM
REMR3NotifyPhysRomRegister(pVM, GCPhys, cb, NULL, fShadowed);
#endif
}
catch (Guest_memory::Region_conflict) { return VERR_PGM_MAPPING_CONFLICT; }
catch (Genode::Out_of_ram) { return VERR_PGM_MAPPING_CONFLICT; }
catch (Genode::Out_of_caps) { return VERR_PGM_MAPPING_CONFLICT; }
return VINF_SUCCESS;
}
int PGMPhysWrite(PVM pVM, RTGCPHYS GCPhys, const void *pvBuf, size_t cbWrite)
{
void *pv = guest_memory()->lookup(GCPhys, cbWrite);
if (pv) {
void * pvx = vmm_memory()->lookup(GCPhys, cbWrite);
Assert(!pvx);
memcpy(pv, pvBuf, cbWrite);
return VINF_SUCCESS;
}
PFNPGMR3PHYSHANDLER pfnHandlerR3 = 0;
void *pvUserR3 = 0;
pv = vmm_memory()->lookup(GCPhys, cbWrite, &pfnHandlerR3, &pvUserR3);
if (!pv || !pfnHandlerR3 || !pvUserR3) {
Genode::error(__func__, " skipped: GCPhys=", Genode::Hex(GCPhys));
return VERR_GENERAL_FAILURE;
}
int rc = pfnHandlerR3(pVM, GCPhys, 0, 0, cbWrite, PGMACCESSTYPE_WRITE,
pvUserR3);
if (rc != VINF_PGM_HANDLER_DO_DEFAULT) {
Genode::error(__FUNCTION__, " unexpected return code ", rc);
return VERR_GENERAL_FAILURE;
}
memcpy(pv, pvBuf, cbWrite);
return VINF_SUCCESS;
}
int PGMR3PhysWriteExternal(PVM pVM, RTGCPHYS GCPhys, const void *pvBuf,
size_t cbWrite, const char *pszWho)
{
VM_ASSERT_OTHER_THREAD(pVM);
return PGMPhysWrite(pVM, GCPhys, pvBuf, cbWrite);
}
int PGMPhysRead(PVM pVM, RTGCPHYS GCPhys, void *pvBuf, size_t cbRead)
{
void *pv = guest_memory()->lookup(GCPhys, cbRead);
if (pv) {
void * pvx = vmm_memory()->lookup(GCPhys, cbRead);
Assert(!pvx);
memcpy(pvBuf, pv, cbRead);
return VINF_SUCCESS;
}
PFNPGMR3PHYSHANDLER pfnHandlerR3 = 0;
void *pvUserR3 = 0;
pv = vmm_memory()->lookup(GCPhys, cbRead, &pfnHandlerR3, &pvUserR3);
if (!pv || !pfnHandlerR3 || !pvUserR3) {
Genode::error("PGMPhysRead skipped: GCPhys=", Genode::Hex(GCPhys));
return VERR_GENERAL_FAILURE;
}
memcpy(pvBuf, pv, cbRead);
return VINF_SUCCESS;
}
int PGMR3PhysReadExternal(PVM pVM, RTGCPHYS GCPhys, void *pvBuf, size_t cbRead)
{
VM_ASSERT_OTHER_THREAD(pVM);
return PGMPhysRead(pVM, GCPhys, pvBuf, cbRead);
}
int PGMR3PhysMMIO2Register(PVM pVM, PPDMDEVINS pDevIns, uint32_t iRegion,
RTGCPHYS cb, uint32_t fFlags, void **ppv, const char
*pszDesc)
{
*ppv = vmm_memory()->alloc((size_t)cb, pDevIns, iRegion);
return VINF_SUCCESS;
}
int PGMR3PhysMMIO2Deregister(PVM pVM, PPDMDEVINS pDevIns, uint32_t iRegion)
{
Genode::warning(__func__, ": pDevIns ", pDevIns, " iRegion=", iRegion);
return VINF_SUCCESS;
}
int PGMR3PhysMMIO2Map(PVM pVM, PPDMDEVINS pDevIns, uint32_t iRegion,
RTGCPHYS GCPhys)
{
size_t cb = vmm_memory()->map_to_vm(pDevIns, GCPhys, iRegion);
if (cb == 0) {
Genode::error(__func__, ": lookup for pDevIns=", pDevIns, " iRegion=",
iRegion, " failed");
Assert(cb);
}
#ifdef VBOX_WITH_REM
REMR3NotifyPhysRamRegister(pVM, GCPhys, cb, REM_NOTIFY_PHYS_RAM_FLAGS_MMIO2);
#endif
return VINF_SUCCESS;
}
int PGMR3PhysMMIO2Unmap(PVM pVM, PPDMDEVINS pDevIns, uint32_t iRegion,
RTGCPHYS GCPhys)
{
RTGCPHYS GCPhysStart = GCPhys;
Genode::size_t size = 1;
bool io = vmm_memory()->lookup_range(GCPhysStart, size);
Assert(io);
Assert(GCPhysStart == GCPhys);
bool INVALIDATE = true;
bool ok = vmm_memory()->unmap_from_vm(GCPhys, size, INVALIDATE);
Assert(ok);
#ifdef VBOX_WITH_REM
REMR3NotifyPhysRamDeregister(pVM, GCPhysStart, size);
#endif
return VINF_SUCCESS;
}
bool PGMR3PhysMMIO2IsBase(PVM pVM, PPDMDEVINS pDevIns, RTGCPHYS GCPhys)
{
return vmm_memory()->lookup(GCPhys, 1);
}
int PGMR3HandlerPhysicalRegister(PVM pVM, PGMPHYSHANDLERTYPE enmType,
RTGCPHYS GCPhys, RTGCPHYS GCPhysLast,
PFNPGMR3PHYSHANDLER pfnHandlerR3,
void *pvUserR3, const char *pszModR0,
const char *pszHandlerR0, RTR0PTR pvUserR0,
const char *pszModRC,
const char *pszHandlerRC,
RTRCPTR pvUserRC, const char *pszDesc)
{
bool ok = vmm_memory()->add_handler(GCPhys, GCPhysLast - GCPhys + 1,
pfnHandlerR3, pvUserR3, enmType);
Assert(ok);
#ifdef VBOX_WITH_REM
REMR3NotifyHandlerPhysicalRegister(pVM, enmType, GCPhys, GCPhysLast -
GCPhys + 1, !!pfnHandlerR3);
#endif
return VINF_SUCCESS;
}
int PGMHandlerPhysicalDeregister(PVM pVM, RTGCPHYS GCPhys)
{
Genode::size_t size = 1;
#ifdef VBOX_WITH_REM
PFNPGMR3PHYSHANDLER pfnHandlerR3 = 0;
PGMPHYSHANDLERTYPE enmType = PGMPHYSHANDLERTYPE_MMIO;
void * pv = vmm_memory()->lookup(GCPhys, size, &pfnHandlerR3, 0, &enmType);
Assert(pv);
#endif
bool ok = vmm_memory()->add_handler(GCPhys, size, 0, 0);
Assert(ok);
#ifdef VBOX_WITH_REM
bool fRestoreAsRAM = pfnHandlerR3 && enmType != PGMPHYSHANDLERTYPE_MMIO;
/* GCPhysstart and size gets written ! */
RTGCPHYS GCPhysStart = GCPhys;
bool io = vmm_memory()->lookup_range(GCPhysStart, size);
Assert(io);
REMR3NotifyHandlerPhysicalDeregister(pVM, enmType, GCPhysStart, size,
!!pfnHandlerR3, fRestoreAsRAM);
#endif
return VINF_SUCCESS;
}
int PGMR3PhysRegisterRam(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS cb,
const char *pszDesc)
{
try {
/*
* XXX Is this function the right place for the allocation?
* The lack of allocation-related VERR_PGM_ error codes suggests
* so.
*/
size_t size = (size_t)cb;
Assert(cb == size);
void *pv = vmm_memory()->alloc_ram(size);
guest_memory()->add_ram_mapping(GCPhys, cb, pv);
#ifdef VBOX_WITH_REM
REMR3NotifyPhysRamRegister(pVM, GCPhys, cb, REM_NOTIFY_PHYS_RAM_FLAGS_RAM);
#endif
}
catch (Guest_memory::Region_conflict) {
return VERR_PGM_MAPPING_CONFLICT; }
catch (Genode::Out_of_ram) {
return VERR_PGM_MAPPING_CONFLICT; /* XXX use a better error code? */ }
catch (Genode::Out_of_caps) {
Genode::warning("Out_of_caps during 'add_ram_mapping'");
return VERR_PGM_MAPPING_CONFLICT; /* XXX use a better error code? */ }
catch (Genode::Region_map::Region_conflict) {
Genode::warning("Region_conflict during 'add_ram_mapping'");
return VERR_PGM_MAPPING_CONFLICT; /* XXX use a better error code? */ }
return VINF_SUCCESS;
}
int PGMMapSetPage(PVM pVM, RTGCPTR GCPtr, uint64_t cb, uint64_t fFlags)
{
return VINF_SUCCESS;
}
RTHCPHYS PGMGetHyperCR3(PVMCPU pVCpu)
{
return 1;
}
int PGMR3Init(PVM pVM)
{
/*
* Satisfy assertion in VMMR3Init. Normally called via:
*
* PGMR3Init -> pgmR3InitPaging -> pgmR3ModeDataInit -> InitData -> MapCR3
*/
for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++) {
PVMCPU pVCpu = &pVM->aCpus[idCpu];
CPUMSetHyperCR3(pVCpu, PGMGetHyperCR3(pVCpu));
pVCpu->pgm.s.fA20Enabled = true;
pVCpu->pgm.s.GCPhysA20Mask = ~((RTGCPHYS)!pVCpu->pgm.s.fA20Enabled << 20);
}
int rc = PDMR3CritSectInit(pVM, &pVM->pgm.s.CritSectX, RT_SRC_POS, "PGM");
AssertRCReturn(rc, rc);
return VINF_SUCCESS;
}
int PGMR3Term(PVM pVM) { return VINF_SUCCESS; }
int PGMPhysGCPtr2CCPtrReadOnly(PVMCPU pVCpu, RTGCPTR GCPtr, void const **ppv,
PPGMPAGEMAPLOCK pLock)
{
Genode::error(__func__, " not implemented - caller ",
__builtin_return_address(0));
Assert(!"not implemented");
return VERR_GENERAL_FAILURE;
}
int PGMR3PhysTlbGCPhys2Ptr(PVM pVM, RTGCPHYS GCPhys, bool fWritable, void **ppv)
{
size_t const size = 1;
PFNPGMR3PHYSHANDLER pfnHandlerR3 = 0;
void *pvUserR3 = 0;
PGMPHYSHANDLERTYPE enmType;
void * pv = vmm_memory()->lookup(GCPhys, size, &pfnHandlerR3, &pvUserR3,
&enmType);
if (!pv) {
/* It could be ordinary guest memory - look it up. */
pv = guest_memory()->lookup(GCPhys, size);
if (!pv) {
Genode::error(__func__, ": lookup for GCPhys=",
Genode::Hex(GCPhys), " failed");
return VERR_PGM_PHYS_TLB_UNASSIGNED;
}
*ppv = pv;
return VINF_SUCCESS;
}
/* pv valid - check handlers next */
if (!pfnHandlerR3 && !pvUserR3) {
*ppv = pv;
return VINF_SUCCESS;
}
if (enmType == PGMPHYSHANDLERTYPE_PHYSICAL_WRITE) {
*ppv = pv;
return VINF_PGM_PHYS_TLB_CATCH_WRITE;
}
Genode::error(__func__, ": denied access - handlers set - GCPhys=",
Genode::Hex(GCPhys));
return VERR_PGM_PHYS_TLB_CATCH_ALL;
}
void PGMR3PhysSetA20(PVMCPU pVCpu, bool fEnable)
{
if (!pVCpu->pgm.s.fA20Enabled != fEnable) {
pVCpu->pgm.s.fA20Enabled = fEnable;
#ifdef VBOX_WITH_REM
REMR3A20Set(pVCpu->pVMR3, pVCpu, fEnable);
#endif
}
return;
}
bool PGMPhysIsA20Enabled(PVMCPU pVCpu)
{
return pVCpu->pgm.s.fA20Enabled;
}
template <typename T>
static void PGMR3PhysWrite(PVM pVM, RTGCPHYS GCPhys, T value)
{
VM_ASSERT_EMT(pVM);
void *pv = guest_memory()->lookup(GCPhys, sizeof(value));
if (!pv) {
Genode::error(__func__, ": invalid write attempt GCPhys=",
Genode::Hex(GCPhys));
return;
}
/* sanity check */
void * pvx = vmm_memory()->lookup(GCPhys, sizeof(value));
Assert(!pvx);
*reinterpret_cast<T *>(pv) = value;
}
void PGMR3PhysWriteU8(PVM pVM, RTGCPHYS GCPhys, uint8_t value)
{
PGMR3PhysWrite(pVM, GCPhys, value);
}
void PGMR3PhysWriteU16(PVM pVM, RTGCPHYS GCPhys, uint16_t value)
{
PGMR3PhysWrite(pVM, GCPhys, value);
}
void PGMR3PhysWriteU32(PVM pVM, RTGCPHYS GCPhys, uint32_t value)
{
PGMR3PhysWrite(pVM, GCPhys, value);
}
template <typename T>
static T PGMR3PhysRead(PVM pVM, RTGCPHYS GCPhys)
{
void *pv = guest_memory()->lookup(GCPhys, sizeof(T));
if (!pv) {
Genode::error(__func__, ": invalid read attempt GCPhys=",
Genode::Hex(GCPhys));
return 0;
}
/* sanity check */
void * pvx = vmm_memory()->lookup(GCPhys, sizeof(T));
Assert(!pvx);
return *reinterpret_cast<T *>(pv);
}
uint64_t PGMR3PhysReadU64(PVM pVM, RTGCPHYS GCPhys)
{
return PGMR3PhysRead<uint64_t>(pVM, GCPhys);
}
uint32_t PGMR3PhysReadU32(PVM pVM, RTGCPHYS GCPhys)
{
return PGMR3PhysRead<uint32_t>(pVM, GCPhys);
}
int PGMPhysGCPhys2CCPtrReadOnly(PVM pVM, RTGCPHYS GCPhys, void const **ppv,
PPGMPAGEMAPLOCK pLock)
{
void *pv = guest_memory()->lookup(GCPhys, 0x1000);
if (!pv) {
Genode::error("unknown address GCPhys=", Genode::Hex(GCPhys));
guest_memory()->dump();
return VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS;
}
*ppv = pv;
return VINF_SUCCESS;
}
int PGMHandlerPhysicalReset(PVM, RTGCPHYS GCPhys)
{
size_t size = 1;
if (!vmm_memory()->unmap_from_vm(GCPhys, size))
Genode::warning(__func__, ": unbacked region - GCPhys ",
Genode::Hex(GCPhys));
return VINF_SUCCESS;
}
extern "C" int MMIO2_MAPPED_SYNC(PVM pVM, RTGCPHYS GCPhys, size_t cbWrite,
void **ppv, Genode::Flexpage_iterator &fli,
bool &writeable)
{
using Genode::Flexpage_iterator;
using Genode::addr_t;
/* DON'T USE normal printf in this function - corrupts unsaved UTCB !!! */
PFNPGMR3PHYSHANDLER pfnHandlerR3 = 0;
void *pvUserR3 = 0;
void * pv = vmm_memory()->lookup(GCPhys, cbWrite, &pfnHandlerR3, &pvUserR3);
if (!pv)
return VERR_PGM_PHYS_TLB_UNASSIGNED;
fli = Flexpage_iterator((addr_t)pv, cbWrite, GCPhys, cbWrite, GCPhys);
if (!pfnHandlerR3 && !pvUserR3) {
*ppv = pv;
/* you may map it */
return VINF_SUCCESS;
}
if (pfnHandlerR3 && pvUserR3) {
int rc = pfnHandlerR3(pVM, GCPhys, 0, 0, cbWrite, PGMACCESSTYPE_WRITE,
pvUserR3);
if (rc == VINF_PGM_HANDLER_DO_DEFAULT) {
*ppv = pv;
/* you may map it */
return VINF_SUCCESS;
}
return rc;
}
RTGCPHYS map_start = GCPhys;
Genode::size_t map_size = 1;
bool io = vmm_memory()->lookup_range(map_start, map_size);
Assert(io);
pv = vmm_memory()->lookup(map_start, map_size);
Assert(pv);
fli = Flexpage_iterator((addr_t)pv, map_size, map_start, map_size, map_start);
*ppv = pv;
writeable = false;
return VINF_SUCCESS;
}
/**
* Resets a virtual CPU when unplugged.
*
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
*/
VMMR3DECL(void) PGMR3ResetCpu(PVM pVM, PVMCPU pVCpu)
{
int rc = PGMR3ChangeMode(pVM, pVCpu, PGMMODE_REAL);
AssertRC(rc);
/*
* Re-init other members.
*/
pVCpu->pgm.s.fA20Enabled = true;
pVCpu->pgm.s.GCPhysA20Mask = ~((RTGCPHYS)!pVCpu->pgm.s.fA20Enabled << 20);
/*
* Clear the FFs PGM owns.
*/
VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_PGM_SYNC_CR3);
VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_PGM_SYNC_CR3_NON_GLOBAL);
}
void PGMR3Reset(PVM pVM)
{
VM_ASSERT_EMT(pVM);
for (VMCPUID i = 0; i < pVM->cCpus; i++)
{
// int rc = PGMR3ChangeMode(pVM, &pVM->aCpus[i], PGMMODE_REAL);
// AssertRC(rc);
}
for (VMCPUID i = 0; i < pVM->cCpus; i++)
{
PVMCPU pVCpu = &pVM->aCpus[i];
VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_PGM_SYNC_CR3);
VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_PGM_SYNC_CR3_NON_GLOBAL);
VMCPU_FF_SET(pVCpu, VMCPU_FF_TLB_FLUSH);
if (!pVCpu->pgm.s.fA20Enabled)
{
pVCpu->pgm.s.fA20Enabled = true;
pVCpu->pgm.s.GCPhysA20Mask = ~((RTGCPHYS)!pVCpu->pgm.s.fA20Enabled << 20);
#ifdef PGM_WITH_A20
pVCpu->pgm.s.fSyncFlags |= PGM_SYNC_UPDATE_PAGE_BIT_VIRTUAL;
VMCPU_FF_SET(pVCpu, VMCPU_FF_PGM_SYNC_CR3);
HMFlushTLB(pVCpu);
#endif
}
}
vmm_memory()->revoke_all();
}
int PGMR3MappingsSize(PVM pVM, uint32_t *pcb)
{
*pcb = 0;
return 0;
}
void PGMR3MemSetup(PVM pVM, bool fAtReset) { }
VMMDECL(bool) PGMIsLockOwner(PVM pVM)
{
return PDMCritSectIsOwner(&pVM->pgm.s.CritSectX);
}
VMM_INT_DECL(void) PGMNotifyNxeChanged(PVMCPU pVCpu, bool fNxe) { }