mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-31 00:24:51 +00:00
parent
4782fd34f6
commit
0ddc69d370
@ -3,6 +3,7 @@ include $(REP_DIR)/lib/mk/virtualbox-common.inc
|
||||
SRC_CC = sup.cc
|
||||
|
||||
INC_DIR += $(call select_from_repositories,src/lib/libc)
|
||||
INC_DIR += $(call select_from_repositories,src/lib/pthread)
|
||||
|
||||
INC_DIR += $(VBOX_DIR)/VMM/include
|
||||
INC_DIR += $(REP_DIR)/src/virtualbox
|
||||
|
@ -25,6 +25,9 @@
|
||||
#include "sup.h"
|
||||
#include "vmm_memory.h"
|
||||
|
||||
/* Libc include */
|
||||
#include <pthread.h>
|
||||
|
||||
/* VirtualBox SUPLib interface */
|
||||
|
||||
int SUPR3QueryVTxSupported(void)
|
||||
@ -77,6 +80,16 @@ int SUPR3CallVMMR0Ex(PVMR0 pVMR0, VMCPUID idCpu, unsigned
|
||||
}
|
||||
|
||||
|
||||
extern "C"
|
||||
bool create_emt_vcpu(pthread_t * thread, size_t stack_size,
|
||||
const pthread_attr_t *attr,
|
||||
void *(*start_routine)(void *), void *arg)
|
||||
{
|
||||
/* no hardware acceleration support */
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dummies and unimplemented stuff.
|
||||
*/
|
||||
@ -85,13 +98,12 @@ uint64_t genode_cpu_hz() {
|
||||
return 1000000000ULL; /* XXX fixed 1GHz return value */
|
||||
}
|
||||
|
||||
|
||||
bool Vmm_memory::unmap_from_vm(RTGCPHYS GCPhys)
|
||||
{
|
||||
PWRN("%s unimplemented", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" int pthread_yield() {
|
||||
PWRN("%s unimplemented", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" void pthread_yield() { PWRN("%s unimplemented", __func__); }
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <base/flex_iterator.h>
|
||||
#include <rom_session/connection.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <os/attached_rom_dataspace.h>
|
||||
|
||||
#include <vmm/vcpu_thread.h>
|
||||
#include <vmm/vcpu_dispatcher.h>
|
||||
@ -51,69 +52,32 @@ static Genode::Semaphore *r0_halt_sem()
|
||||
|
||||
/* Genode specific function */
|
||||
|
||||
void SUPR3QueryHWACCLonGenodeSupport(VM * pVM) {
|
||||
static Genode::Attached_rom_dataspace hip_rom("hypervisor_info_page");
|
||||
|
||||
void SUPR3QueryHWACCLonGenodeSupport(VM * pVM)
|
||||
{
|
||||
try {
|
||||
using namespace Genode;
|
||||
|
||||
Rom_connection hip_rom("hypervisor_info_page");
|
||||
|
||||
Nova::Hip * const hip = env()->rm_session()->attach(hip_rom.dataspace());
|
||||
|
||||
if (!hip)
|
||||
return;
|
||||
Nova::Hip * hip = hip_rom.local_addr<Nova::Hip>();
|
||||
|
||||
pVM->hwaccm.s.svm.fSupported = hip->has_feature_svm();
|
||||
pVM->hwaccm.s.vmx.fSupported = hip->has_feature_vmx();
|
||||
|
||||
PINF("support svm %u vmx %u", hip->has_feature_svm(), hip->has_feature_vmx());
|
||||
} catch (...) {
|
||||
PWRN("No hardware acceleration available - execution will be slow!");
|
||||
} /* if we get an exception let hardware support off */
|
||||
}
|
||||
|
||||
|
||||
void SUPR3QueryHWACCLonGenodeCreateVM(VM * pVM)
|
||||
{
|
||||
bool svm = pVM->hwaccm.s.svm.fSupported;
|
||||
|
||||
if (!svm && !pVM->hwaccm.s.vmx.fSupported) {
|
||||
PERR("SVM nor VMX supported by hardware accelerated code called !");
|
||||
return;
|
||||
}
|
||||
|
||||
Assert(!vcpu_handler);
|
||||
|
||||
if (svm)
|
||||
vcpu_handler = new Vcpu_handler_svm();
|
||||
else
|
||||
vcpu_handler = new Vcpu_handler_vmx();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* VirtualBox SUPLib interface */
|
||||
int SUPR3QueryVTxSupported(void)
|
||||
{
|
||||
return VINF_SUCCESS;
|
||||
}
|
||||
int SUPR3QueryVTxSupported(void) { return VINF_SUCCESS; }
|
||||
|
||||
|
||||
int SUPR3CallVMMR0Fast(PVMR0 pVMR0, unsigned uOperation, VMCPUID idCpu)
|
||||
{
|
||||
switch (uOperation)
|
||||
{
|
||||
case SUP_VMMR0_DO_HWACC_RUN:
|
||||
{
|
||||
|
||||
VM * pVM = reinterpret_cast<VM *>(pVMR0);
|
||||
PVMCPU pVCpu = &pVM->aCpus[idCpu];
|
||||
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
|
||||
|
||||
return vcpu_handler->run_hw(pVMR0, idCpu);
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
switch (uOperation) {
|
||||
case SUP_VMMR0_DO_HWACC_RUN:
|
||||
return vcpu_handler->run_hw(pVMR0, idCpu);
|
||||
}
|
||||
return VERR_INTERNAL_ERROR;
|
||||
}
|
||||
@ -122,58 +86,43 @@ int SUPR3CallVMMR0Fast(PVMR0 pVMR0, unsigned uOperation, VMCPUID idCpu)
|
||||
int SUPR3CallVMMR0Ex(PVMR0 pVMR0, VMCPUID idCpu, unsigned
|
||||
uOperation, uint64_t u64Arg, PSUPVMMR0REQHDR pReqHdr)
|
||||
{
|
||||
static unsigned counter = 0;
|
||||
switch (uOperation) {
|
||||
|
||||
switch(uOperation)
|
||||
{
|
||||
case VMMR0_DO_GVMM_CREATE_VM:
|
||||
genode_VMMR0_DO_GVMM_CREATE_VM(pReqHdr);
|
||||
return VINF_SUCCESS;
|
||||
case VMMR0_DO_GVMM_CREATE_VM:
|
||||
genode_VMMR0_DO_GVMM_CREATE_VM(pReqHdr);
|
||||
return VINF_SUCCESS;
|
||||
|
||||
case VMMR0_DO_GVMM_SCHED_HALT:
|
||||
// counter ++;
|
||||
// PERR("halt %u", counter);
|
||||
r0_halt_sem()->down();
|
||||
// PERR("halt - done");
|
||||
return VINF_SUCCESS;
|
||||
case VMMR0_DO_GVMM_SCHED_HALT:
|
||||
r0_halt_sem()->down();
|
||||
return VINF_SUCCESS;
|
||||
|
||||
case VMMR0_DO_GVMM_SCHED_WAKE_UP:
|
||||
// counter ++;
|
||||
// PERR("sched wake up %u", counter);
|
||||
r0_halt_sem()->up();
|
||||
return VINF_SUCCESS;
|
||||
case VMMR0_DO_GVMM_SCHED_WAKE_UP:
|
||||
r0_halt_sem()->up();
|
||||
return VINF_SUCCESS;
|
||||
|
||||
case VMMR0_DO_GVMM_SCHED_POLL:
|
||||
/* called by 'vmR3HaltGlobal1Halt' */
|
||||
// PDBG("SUPR3CallVMMR0Ex: VMMR0_DO_GVMM_SCHED_POLL");
|
||||
return VINF_SUCCESS;
|
||||
/* called by 'vmR3HaltGlobal1Halt' */
|
||||
case VMMR0_DO_GVMM_SCHED_POLL:
|
||||
return VINF_SUCCESS;
|
||||
|
||||
case VMMR0_DO_VMMR0_INIT:
|
||||
{
|
||||
VM * pVM = reinterpret_cast<VM *>(pVMR0);
|
||||
SUPR3QueryHWACCLonGenodeSupport(pVM);
|
||||
return VINF_SUCCESS;
|
||||
}
|
||||
case VMMR0_DO_HWACC_SETUP_VM:
|
||||
{
|
||||
VM * pVM = reinterpret_cast<VM *>(pVMR0);
|
||||
SUPR3QueryHWACCLonGenodeCreateVM(pVM);
|
||||
return VINF_SUCCESS;
|
||||
}
|
||||
case VMMR0_DO_HWACC_ENABLE:
|
||||
return VINF_SUCCESS;
|
||||
case VMMR0_DO_VMMR0_INIT:
|
||||
SUPR3QueryHWACCLonGenodeSupport(reinterpret_cast<VM *>(pVMR0));
|
||||
return VINF_SUCCESS;
|
||||
|
||||
case VMMR0_DO_GVMM_SCHED_POKE:
|
||||
{
|
||||
/* XXX only do one of it - either recall or up - not both XXX */
|
||||
vcpu_handler->recall();
|
||||
r0_halt_sem()->up();
|
||||
return VINF_SUCCESS;
|
||||
}
|
||||
case VMMR0_DO_HWACC_SETUP_VM:
|
||||
return VINF_SUCCESS;
|
||||
|
||||
default:
|
||||
PERR("SUPR3CallVMMR0Ex: unhandled uOperation %d", uOperation);
|
||||
return VERR_GENERAL_FAILURE;
|
||||
case VMMR0_DO_HWACC_ENABLE:
|
||||
return VINF_SUCCESS;
|
||||
|
||||
/* XXX only do one of it - either recall or up - not both XXX */
|
||||
case VMMR0_DO_GVMM_SCHED_POKE:
|
||||
vcpu_handler->recall();
|
||||
r0_halt_sem()->up();
|
||||
return VINF_SUCCESS;
|
||||
|
||||
default:
|
||||
PERR("SUPR3CallVMMR0Ex: unhandled uOperation %d", uOperation);
|
||||
return VERR_GENERAL_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,7 +130,8 @@ int SUPR3CallVMMR0Ex(PVMR0 pVMR0, VMCPUID idCpu, unsigned
|
||||
/**
|
||||
* Various support stuff - base-nova specific.
|
||||
*/
|
||||
uint64_t genode_cpu_hz() {
|
||||
uint64_t genode_cpu_hz()
|
||||
{
|
||||
static uint64_t cpu_freq = 0;
|
||||
|
||||
if (!cpu_freq) {
|
||||
@ -205,12 +155,6 @@ uint64_t genode_cpu_hz() {
|
||||
}
|
||||
|
||||
|
||||
extern "C" int pthread_yield() {
|
||||
Nova::ec_ctrl(Nova::EC_YIELD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool Vmm_memory::unmap_from_vm(RTGCPHYS GCPhys)
|
||||
{
|
||||
size_t const size = 1;
|
||||
@ -232,3 +176,27 @@ bool Vmm_memory::unmap_from_vm(RTGCPHYS GCPhys)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
extern "C" void pthread_yield(void) { Nova::ec_ctrl(Nova::EC_YIELD); }
|
||||
|
||||
|
||||
extern "C"
|
||||
bool create_emt_vcpu(pthread_t * pthread, size_t stack,
|
||||
const pthread_attr_t *attr,
|
||||
void *(*start_routine)(void *), void *arg)
|
||||
{
|
||||
Nova::Hip * hip = hip_rom.local_addr<Nova::Hip>();
|
||||
|
||||
if (!hip->has_feature_vmx() && !hip->has_feature_svm())
|
||||
return false;
|
||||
|
||||
if (hip->has_feature_vmx())
|
||||
vcpu_handler = new Vcpu_handler_vmx(stack, attr, start_routine, arg);
|
||||
|
||||
if (hip->has_feature_svm())
|
||||
vcpu_handler = new Vcpu_handler_svm(stack, attr, start_routine, arg);
|
||||
|
||||
*pthread = vcpu_handler;
|
||||
return true;
|
||||
}
|
||||
|
@ -39,6 +39,12 @@
|
||||
#include "guest_memory.h"
|
||||
#include "vmm_memory.h"
|
||||
|
||||
/* Genode libc pthread binding */
|
||||
#include "thread.h"
|
||||
|
||||
/* LibC includes */
|
||||
#include <setjmp.h>
|
||||
|
||||
/*
|
||||
* VirtualBox stores segment attributes in Intel format using a 32-bit
|
||||
* value. NOVA represents the attributes in packet format using a 16-bit
|
||||
@ -55,21 +61,22 @@ static inline Genode::uint32_t sel_ar_conv_from_nova(Genode::uint16_t v)
|
||||
return (v & 0xff) | (((uint32_t )v << 4) & 0x1f000);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Used to map memory for virtual framebuffer to VM
|
||||
*/
|
||||
extern "C" int MMIO2_MAPPED_SYNC(PVM pVM, RTGCPHYS GCPhys, size_t cbWrite);
|
||||
|
||||
|
||||
class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
|
||||
class Vcpu_handler : public Vmm::Vcpu_dispatcher<pthread>
|
||||
{
|
||||
private:
|
||||
|
||||
enum { STACK_SIZE = 4096 };
|
||||
|
||||
Genode::Cap_connection _cap_connection;
|
||||
Vmm::Vcpu_other_pd _vcpu;
|
||||
|
||||
Genode::addr_t _ec_sel = 0;
|
||||
|
||||
|
||||
void fpu_save(char * data) {
|
||||
Assert(!(reinterpret_cast<Genode::addr_t>(data) & 0xF));
|
||||
asm volatile ("fxsave %0" : "=m" (*data));
|
||||
@ -82,41 +89,48 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
|
||||
|
||||
protected:
|
||||
|
||||
/* unlocked by first startup exception */
|
||||
Genode::Lock _lock_startup;
|
||||
Genode::Lock _signal_vcpu;
|
||||
Genode::Lock _signal_emt;
|
||||
struct {
|
||||
Nova::mword_t mtd;
|
||||
unsigned intr_state;
|
||||
unsigned ctrl[2];
|
||||
} next_utcb;
|
||||
|
||||
PVM _current_vm;
|
||||
PVMCPU _current_vcpu;
|
||||
unsigned _current_exit_cond;
|
||||
PVM _current_vm;
|
||||
PVMCPU _current_vcpu;
|
||||
void * _stack_reply;
|
||||
jmp_buf _env;
|
||||
|
||||
__attribute__((noreturn)) void _default_handler(unsigned cond)
|
||||
void switch_to_hw(PCPUMCTX pCtx) {
|
||||
unsigned long value;
|
||||
|
||||
if (!setjmp(_env)) {
|
||||
_stack_reply = reinterpret_cast<void *>(&value - 1);
|
||||
Nova::reply(_stack_reply);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _irq_window(unsigned cond)
|
||||
{
|
||||
using namespace Nova;
|
||||
using namespace Genode;
|
||||
Nova::Utcb * utcb = reinterpret_cast<Nova::Utcb *>(Thread_base::utcb());
|
||||
|
||||
Thread_base *myself = Thread_base::myself();
|
||||
Utcb *utcb = reinterpret_cast<Utcb *>(myself->utcb());
|
||||
Assert(!(utcb->intr_state & 3));
|
||||
Assert(utcb->flags & X86_EFL_IF);
|
||||
|
||||
/* tell caller what happened */
|
||||
_current_exit_cond = cond;
|
||||
if (irq_win(utcb)) {
|
||||
/* reset mtd to not transfer anything back by accident */
|
||||
utcb->mtd = 0;
|
||||
/* inject IRQ */
|
||||
if (inj_event(utcb, _current_vcpu))
|
||||
Nova::reply(_stack_reply);
|
||||
}
|
||||
|
||||
PVMCPU pVCpu = _current_vcpu;
|
||||
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
|
||||
/* go back to re-compiler */
|
||||
longjmp(_env, 1);
|
||||
}
|
||||
|
||||
fpu_save(reinterpret_cast<char *>(&pCtx->fpu));
|
||||
|
||||
/* unblock caller */
|
||||
_signal_emt.unlock();
|
||||
|
||||
/* block myself */
|
||||
_signal_vcpu.lock();
|
||||
|
||||
fpu_load(reinterpret_cast<char *>(&pCtx->fpu));
|
||||
utcb->mtd |= Mtd::FPU;
|
||||
|
||||
Nova::reply(myself->stack_top());
|
||||
__attribute__((noreturn)) void _default_handler()
|
||||
{
|
||||
longjmp(_env, 1);
|
||||
}
|
||||
|
||||
|
||||
@ -128,13 +142,16 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
|
||||
using namespace Nova;
|
||||
using namespace Genode;
|
||||
|
||||
addr_t stack_top;
|
||||
|
||||
Assert(utcb->actv_state == 0);
|
||||
Assert(!(utcb->intr_state & 3));
|
||||
|
||||
Assert(!(utcb->inj_info & 0x80000000));
|
||||
|
||||
if (unmap) {
|
||||
PERR("unmap not implemented\n");
|
||||
|
||||
/* deadlock until implemented */
|
||||
_signal_vcpu.lock();
|
||||
|
||||
Nova::reply(myself->stack_top());
|
||||
Nova::reply(reinterpret_cast<void *>(&stack_top));
|
||||
}
|
||||
|
||||
Flexpage_iterator fli;
|
||||
@ -159,26 +176,8 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
|
||||
}
|
||||
|
||||
/* emulator has to take over if fault region is not ram */
|
||||
if (!pv) {
|
||||
/* tell caller what happened */
|
||||
_current_exit_cond = NPT_EPT;
|
||||
|
||||
PVMCPU pVCpu = _current_vcpu;
|
||||
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
|
||||
|
||||
fpu_save(reinterpret_cast<char *>(&pCtx->fpu));
|
||||
|
||||
/* unblock caller */
|
||||
_signal_emt.unlock();
|
||||
|
||||
/* block myself */
|
||||
_signal_vcpu.lock();
|
||||
|
||||
fpu_load(reinterpret_cast<char *>(&pCtx->fpu));
|
||||
utcb->mtd |= Mtd::FPU;
|
||||
|
||||
Nova::reply(myself->stack_top());
|
||||
}
|
||||
if (!pv)
|
||||
longjmp(_env, 1);
|
||||
|
||||
/* fault region is ram - so map it */
|
||||
enum {
|
||||
@ -210,7 +209,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
|
||||
res = utcb->append_item(crd, flexpage.hotspot, USER_PD, GUEST_PGT);
|
||||
} while (res);
|
||||
|
||||
Nova::reply(myself->stack_top());
|
||||
Nova::reply(reinterpret_cast<void *>(&stack_top));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -396,7 +395,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
|
||||
}
|
||||
|
||||
|
||||
inline void inj_event(Nova::Utcb * utcb, PVMCPU pVCpu)
|
||||
inline bool inj_event(Nova::Utcb * utcb, PVMCPU pVCpu)
|
||||
{
|
||||
PCPUMCTX const pCtx = CPUMQueryGuestCtxPtr(pVCpu);
|
||||
|
||||
@ -409,7 +408,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
|
||||
|
||||
if (VMCPU_FF_ISPENDING(pVCpu, (VMCPU_FF_INTERRUPT_APIC|VMCPU_FF_INTERRUPT_PIC))) {
|
||||
|
||||
if (!(utcb->flags & X86_EFL_IF)) {
|
||||
if (!(pCtx->rflags.u & X86_EFL_IF)) {
|
||||
|
||||
unsigned vector = 0;
|
||||
utcb->inj_info = 0x1000 | vector;
|
||||
@ -430,9 +429,9 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
|
||||
}
|
||||
|
||||
/* can an interrupt be dispatched ? */
|
||||
if (!TRPMHasTrap(pVCpu) || !(utcb->flags & X86_EFL_IF) ||
|
||||
if (!TRPMHasTrap(pVCpu) || !(pCtx->rflags.u & X86_EFL_IF) ||
|
||||
VMCPU_FF_ISSET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS))
|
||||
return;
|
||||
return false;
|
||||
|
||||
#ifdef VBOX_STRICT
|
||||
if (TRPMHasTrap(pVCpu)) {
|
||||
@ -472,20 +471,44 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
|
||||
utcb->inj_error = Event.n.u32ErrorCode;
|
||||
|
||||
utcb->mtd |= Nova::Mtd::INJ;
|
||||
|
||||
/*
|
||||
PDBG("type:info:vector %x:%x:%x",
|
||||
Vmm::printf("type:info:vector %x:%x:%x\n",
|
||||
Event.n.u3Type, utcb->inj_info, u8Vector);
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
inline void irq_win(Nova::Utcb * utcb, PVMCPU pVCpu)
|
||||
inline bool irq_win(Nova::Utcb * utcb)
|
||||
{
|
||||
Assert(utcb->flags & X86_EFL_IF);
|
||||
Assert(!(VMCPU_FF_ISSET(_current_vcpu, VMCPU_FF_INHIBIT_INTERRUPTS)));
|
||||
|
||||
Nova::mword_t const mtd = Nova::Mtd::INJ;
|
||||
utcb->mtd = ~mtd;
|
||||
uint32_t check_vm = VM_FF_HWACCM_TO_R3_MASK | VM_FF_REQUEST
|
||||
| VM_FF_PGM_POOL_FLUSH_PENDING
|
||||
| VM_FF_PDM_DMA;
|
||||
uint32_t check_vcpu = VMCPU_FF_HWACCM_TO_R3_MASK
|
||||
| VMCPU_FF_PGM_SYNC_CR3
|
||||
| VMCPU_FF_PGM_SYNC_CR3_NON_GLOBAL
|
||||
| VMCPU_FF_REQUEST;
|
||||
|
||||
if (VM_FF_ISPENDING(_current_vm, check_vm)
|
||||
|| VMCPU_FF_ISPENDING(_current_vcpu, check_vcpu))
|
||||
{
|
||||
Assert(VM_FF_ISPENDING(_current_vm, VM_FF_HWACCM_TO_R3_MASK) ||
|
||||
VMCPU_FF_ISPENDING(_current_vcpu,
|
||||
VMCPU_FF_HWACCM_TO_R3_MASK));
|
||||
|
||||
Assert(!(RT_UNLIKELY(VM_FF_ISPENDING(_current_vm,
|
||||
VM_FF_PGM_NO_MEMORY))));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Is in Realmode ? */
|
||||
if (!(utcb->cr0 & X86_CR0_PE))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool hw_load_state(Nova::Utcb *, VM *, PVMCPU) = 0;
|
||||
@ -505,21 +528,16 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
|
||||
};
|
||||
|
||||
|
||||
Vcpu_handler()
|
||||
Vcpu_handler(size_t stack_size, const pthread_attr_t *attr,
|
||||
void *(*start_routine) (void *), void *arg)
|
||||
:
|
||||
Vmm::Vcpu_dispatcher<Genode::Thread_base>(STACK_SIZE,
|
||||
_cap_connection),
|
||||
_ec_sel(Genode::cap_map()->insert()),
|
||||
_lock_startup(Genode::Lock::LOCKED),
|
||||
_signal_emt(Genode::Lock::LOCKED),
|
||||
_signal_vcpu(Genode::Lock::LOCKED)
|
||||
Vmm::Vcpu_dispatcher<pthread>(stack_size, _cap_connection,
|
||||
attr ? *attr : 0, start_routine, arg),
|
||||
_ec_sel(Genode::cap_map()->insert())
|
||||
{ }
|
||||
|
||||
void start() {
|
||||
_vcpu.start(_ec_sel);
|
||||
|
||||
/* wait until vCPU thread is up */
|
||||
_lock_startup.lock();
|
||||
}
|
||||
|
||||
void recall()
|
||||
@ -623,23 +641,28 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
|
||||
|
||||
Nova::Utcb *utcb = reinterpret_cast<Nova::Utcb *>(Thread_base::utcb());
|
||||
|
||||
Assert(Thread_base::utcb() == Thread_base::myself()->utcb());
|
||||
|
||||
/* take the utcb state prepared during the last exit */
|
||||
utcb->mtd = next_utcb.mtd;
|
||||
utcb->intr_state = next_utcb.intr_state;
|
||||
utcb->actv_state = 0; /* XXX */
|
||||
utcb->ctrl[0] = next_utcb.ctrl[0];
|
||||
utcb->ctrl[1] = next_utcb.ctrl[1];
|
||||
|
||||
using namespace Nova;
|
||||
Genode::Thread_base *myself = Genode::Thread_base::myself();
|
||||
|
||||
/* check whether to inject interrupts */
|
||||
inj_event(utcb, pVCpu);
|
||||
|
||||
/* Transfer vCPU state from vBox to Nova format */
|
||||
if (!vbox_to_utcb(utcb, pVM, pVCpu) ||
|
||||
!hw_load_state(utcb, pVM, pVCpu)) {
|
||||
|
||||
PERR("loading vCPU state failed");
|
||||
/* deadlock here */
|
||||
_signal_emt.lock();
|
||||
return VERR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
/* check whether to inject interrupts */
|
||||
inj_event(utcb, pVCpu);
|
||||
|
||||
ResumeExecution:
|
||||
|
||||
/*
|
||||
* Flag vCPU to be "pokeable" by external events such as interrupts
|
||||
* from virtual devices. Only if this flag is set, the
|
||||
@ -649,17 +672,24 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
|
||||
*/
|
||||
VMCPU_SET_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC);
|
||||
|
||||
this->_current_vm = pVM;
|
||||
this->_current_vcpu = pVCpu;
|
||||
/* write FPU state from pCtx to vCPU */
|
||||
fpu_load(reinterpret_cast<char *>(&pCtx->fpu));
|
||||
|
||||
/* let vCPU run */
|
||||
_signal_vcpu.unlock();
|
||||
utcb->mtd |= Mtd::FPU;
|
||||
|
||||
/* waiting to be woken up */
|
||||
_signal_emt.lock();
|
||||
_current_vm = pVM;
|
||||
_current_vcpu = pVCpu;
|
||||
|
||||
this->_current_vm = 0;
|
||||
this->_current_vcpu = 0;
|
||||
/* switch to hardware accelerated mode */
|
||||
switch_to_hw(pCtx);
|
||||
|
||||
Assert(utcb->actv_state == 0);
|
||||
|
||||
_current_vm = 0;
|
||||
_current_vcpu = 0;
|
||||
|
||||
/* write FPU state of vCPU to pCtx */
|
||||
fpu_save(reinterpret_cast<char *>(&pCtx->fpu));
|
||||
|
||||
// CPUMSetChangedFlags(pVCpu, CPUM_CHANGED_GLOBAL_TLB_FLUSH);
|
||||
|
||||
@ -668,89 +698,23 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
|
||||
/* Transfer vCPU state from Nova to vBox format */
|
||||
if (!utcb_to_vbox(utcb, pVM, pVCpu) ||
|
||||
!hw_save_state(utcb, pVM, pVCpu)) {
|
||||
|
||||
PERR("saving vCPU state failed");
|
||||
/* deadlock here */
|
||||
_signal_emt.lock();
|
||||
return VERR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
/* reset message transfer descriptor for next invocation */
|
||||
utcb->mtd = 0;
|
||||
next_utcb.mtd = 0;
|
||||
next_utcb.intr_state = utcb->intr_state;
|
||||
next_utcb.ctrl[0] = utcb->ctrl[0];
|
||||
next_utcb.ctrl[1] = utcb->ctrl[1];
|
||||
|
||||
if (utcb->intr_state & 3) {
|
||||
/*
|
||||
PDBG("reset intr_state - exit reason %u", _current_exit_cond);
|
||||
*/
|
||||
utcb->intr_state &= ~3;
|
||||
utcb->mtd |= Mtd::STA;
|
||||
if (next_utcb.intr_state & 3) {
|
||||
next_utcb.intr_state &= ~3U;
|
||||
next_utcb.mtd |= Mtd::STA;
|
||||
}
|
||||
|
||||
switch (_current_exit_cond)
|
||||
{
|
||||
case RECALL:
|
||||
|
||||
case VMX_EXIT_EPT_VIOLATION:
|
||||
case VMX_EXIT_PORT_IO:
|
||||
case VMX_EXIT_ERR_INVALID_GUEST_STATE:
|
||||
case VMX_EXIT_HLT:
|
||||
|
||||
case SVM_EXIT_IOIO:
|
||||
case SVM_NPT:
|
||||
case SVM_EXIT_HLT:
|
||||
case SVM_INVALID:
|
||||
case SVM_EXIT_MSR:
|
||||
|
||||
case EMULATE_INSTR:
|
||||
return VINF_EM_RAW_EMULATE_INSTR;
|
||||
|
||||
case SVM_EXIT_VINTR:
|
||||
case VMX_EXIT_IRQ_WINDOW:
|
||||
{
|
||||
if (VMCPU_FF_ISSET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS)) {
|
||||
if (pCtx->rip != EMGetInhibitInterruptsPC(pVCpu))
|
||||
PERR("inhibit interrupts %x %x", pCtx->rip, EMGetInhibitInterruptsPC(pVCpu));
|
||||
}
|
||||
|
||||
uint32_t check_vm = VM_FF_HWACCM_TO_R3_MASK | VM_FF_REQUEST
|
||||
| VM_FF_PGM_POOL_FLUSH_PENDING
|
||||
| VM_FF_PDM_DMA;
|
||||
uint32_t check_vcpu = VMCPU_FF_HWACCM_TO_R3_MASK
|
||||
| VMCPU_FF_PGM_SYNC_CR3
|
||||
| VMCPU_FF_PGM_SYNC_CR3_NON_GLOBAL
|
||||
| VMCPU_FF_REQUEST;
|
||||
|
||||
if (VM_FF_ISPENDING(pVM, check_vm)
|
||||
|| VMCPU_FF_ISPENDING(pVCpu, check_vcpu))
|
||||
{
|
||||
Assert(VM_FF_ISPENDING(pVM, VM_FF_HWACCM_TO_R3_MASK) ||
|
||||
VMCPU_FF_ISPENDING(pVCpu, VMCPU_FF_HWACCM_TO_R3_MASK));
|
||||
|
||||
if (RT_UNLIKELY(VM_FF_ISPENDING(pVM, VM_FF_PGM_NO_MEMORY)))
|
||||
{
|
||||
PERR(" no memory");
|
||||
while (1) {}
|
||||
}
|
||||
|
||||
// PERR(" em raw to r3");
|
||||
return VINF_EM_RAW_TO_R3;
|
||||
}
|
||||
|
||||
if ((utcb->intr_state & 3))
|
||||
PERR("irq window with intr_state %x", utcb->intr_state);
|
||||
|
||||
irq_win(utcb, pVCpu);
|
||||
|
||||
goto ResumeExecution;
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
PERR("unknown exit cond:ip:qual[0],[1] %lx:%lx:%llx:%llx",
|
||||
_current_exit_cond, utcb->ip, utcb->qual[0], utcb->qual[1]);
|
||||
|
||||
while (1) {}
|
||||
}
|
||||
|
||||
return VERR_INTERNAL_ERROR;
|
||||
return VINF_EM_RAW_EMULATE_INSTR;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -19,25 +19,10 @@ class Vcpu_handler_svm : public Vcpu_handler
|
||||
{
|
||||
private:
|
||||
|
||||
__attribute__((noreturn)) void _svm_default() { _default_handler(); }
|
||||
|
||||
__attribute__((noreturn)) void _svm_vintr() {
|
||||
_default_handler(SVM_EXIT_VINTR);
|
||||
}
|
||||
__attribute__((noreturn)) void _svm_rdtsc() {
|
||||
_default_handler(SVM_EXIT_RDTSC);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _svm_msr() {
|
||||
_default_handler(SVM_EXIT_MSR);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _svm_recall()
|
||||
{
|
||||
_default_handler(SVM_INVALID);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _svm_halt()
|
||||
{
|
||||
_default_handler(SVM_EXIT_HLT);
|
||||
_irq_window(SVM_EXIT_VINTR);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _svm_ioio()
|
||||
@ -53,18 +38,14 @@ class Vcpu_handler_svm : public Vcpu_handler
|
||||
|
||||
PERR("invalid gueststate");
|
||||
|
||||
/* deadlock here */
|
||||
_signal_vcpu.lock();
|
||||
|
||||
|
||||
utcb->ctrl[0] = ctrl0;
|
||||
utcb->ctrl[1] = 0;
|
||||
utcb->mtd = Mtd::CTRL;
|
||||
|
||||
Nova::reply(myself->stack_top());
|
||||
Nova::reply(_stack_reply);
|
||||
}
|
||||
|
||||
_default_handler(SVM_EXIT_IOIO);
|
||||
_default_handler();
|
||||
}
|
||||
|
||||
template <unsigned X>
|
||||
@ -84,40 +65,43 @@ class Vcpu_handler_svm : public Vcpu_handler
|
||||
{
|
||||
using namespace Nova;
|
||||
|
||||
Genode::Thread_base *myself = Genode::Thread_base::myself();
|
||||
Utcb *utcb = reinterpret_cast<Utcb *>(myself->utcb());
|
||||
/* enable VM exits for CPUID */
|
||||
next_utcb.mtd = Nova::Mtd::CTRL;
|
||||
next_utcb.ctrl[0] = SVM_CTRL1_INTERCEPT_CPUID;
|
||||
next_utcb.ctrl[1] = 0;
|
||||
|
||||
/* we are ready, unlock our creator */
|
||||
_lock_startup.unlock();
|
||||
void *exit_status = _start_routine(_arg);
|
||||
pthread_exit(exit_status);
|
||||
|
||||
/* wait until EMT thread say so */
|
||||
_signal_vcpu.lock();
|
||||
|
||||
Nova::reply(myself->stack_top());
|
||||
Nova::reply(nullptr);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Vcpu_handler_svm()
|
||||
Vcpu_handler_svm(size_t stack_size, const pthread_attr_t *attr,
|
||||
void *(*start_routine) (void *), void *arg)
|
||||
: Vcpu_handler(stack_size, attr, start_routine, arg)
|
||||
{
|
||||
using namespace Nova;
|
||||
|
||||
typedef Vcpu_handler_svm This;
|
||||
|
||||
register_handler<RECALL, This,
|
||||
&This::_svm_recall>(vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
|
||||
&This::_svm_default>(vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
|
||||
register_handler<SVM_EXIT_IOIO, This,
|
||||
&This::_svm_ioio> (vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
|
||||
register_handler<SVM_EXIT_VINTR, This,
|
||||
&This::_svm_vintr> (vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
|
||||
register_handler<SVM_EXIT_RDTSC, This,
|
||||
&This::_svm_rdtsc> (vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
|
||||
&This::_svm_default> (vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
|
||||
register_handler<SVM_EXIT_MSR, This,
|
||||
&This::_svm_msr> (vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
|
||||
&This::_svm_default> (vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
|
||||
register_handler<SVM_NPT, This,
|
||||
&This::_svm_npt<SVM_NPT>>(vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
|
||||
register_handler<SVM_EXIT_HLT, This,
|
||||
&This::_svm_halt>(vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
|
||||
&This::_svm_default>(vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
|
||||
register_handler<SVM_EXIT_CPUID, This,
|
||||
&This::_svm_default> (vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
|
||||
register_handler<VCPU_STARTUP, This,
|
||||
&This::_svm_startup>(vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
|
||||
|
||||
|
@ -40,36 +40,24 @@ class Vcpu_handler_vmx : public Vcpu_handler
|
||||
utcb->qual[1] & ~((1UL << 12) - 1));
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _vmx_default() { _default_handler(); }
|
||||
|
||||
__attribute__((noreturn)) void _vmx_startup()
|
||||
{
|
||||
Vmm::printf("%s\n", __func__);
|
||||
using namespace Nova;
|
||||
|
||||
Genode::Thread_base *myself = Genode::Thread_base::myself();
|
||||
Utcb *utcb = reinterpret_cast<Utcb *>(myself->utcb());
|
||||
|
||||
/* we are ready, unlock our creator */
|
||||
_lock_startup.unlock();
|
||||
|
||||
/* wait until EMT thread say so */
|
||||
_signal_vcpu.lock();
|
||||
|
||||
/* avoid as many as possible VM exits */
|
||||
utcb->mtd |= Mtd::CTRL;
|
||||
utcb->ctrl[0] = 0;
|
||||
utcb->ctrl[1] = 0;
|
||||
next_utcb.mtd = Nova::Mtd::CTRL;
|
||||
next_utcb.ctrl[0] = 0;
|
||||
next_utcb.ctrl[1] = 0;
|
||||
|
||||
Nova::reply(myself->stack_top());
|
||||
}
|
||||
void *exit_status = _start_routine(_arg);
|
||||
pthread_exit(exit_status);
|
||||
|
||||
__attribute__((noreturn)) void _vmx_recall()
|
||||
{
|
||||
_default_handler(RECALL);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _vmx_pause()
|
||||
{
|
||||
_default_handler(EMULATE_INSTR);
|
||||
Nova::reply(nullptr);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _vmx_triple()
|
||||
@ -79,64 +67,19 @@ class Vcpu_handler_vmx : public Vcpu_handler
|
||||
|
||||
Vmm::printf("triple fault - dead\n");
|
||||
|
||||
_signal_vcpu.lock();
|
||||
|
||||
_default_handler(EMULATE_INSTR);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _vmx_msr_write()
|
||||
{
|
||||
_default_handler(EMULATE_INSTR);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _vmx_msr_read()
|
||||
{
|
||||
_default_handler(EMULATE_INSTR);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _vmx_ioio()
|
||||
{
|
||||
_default_handler(VMX_EXIT_PORT_IO);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _vmx_invalid()
|
||||
{
|
||||
_default_handler(VMX_EXIT_ERR_INVALID_GUEST_STATE);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _vmx_init()
|
||||
{
|
||||
_default_handler(EMULATE_INSTR);
|
||||
_default_handler();
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _vmx_irqwin()
|
||||
{
|
||||
_default_handler(VMX_EXIT_IRQ_WINDOW);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _vmx_hlt()
|
||||
{
|
||||
_default_handler(VMX_EXIT_HLT);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _vmx_cpuid()
|
||||
{
|
||||
_default_handler(EMULATE_INSTR);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _vmx_rdtsc()
|
||||
{
|
||||
_default_handler(EMULATE_INSTR);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void _vmx_vmcall()
|
||||
{
|
||||
_default_handler(EMULATE_INSTR);
|
||||
_irq_window(VMX_EXIT_IRQ_WINDOW);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Vcpu_handler_vmx()
|
||||
Vcpu_handler_vmx(size_t stack_size, const pthread_attr_t *attr,
|
||||
void *(*start_routine) (void *), void *arg)
|
||||
: Vcpu_handler(stack_size, attr, start_routine, arg)
|
||||
{
|
||||
using namespace Nova;
|
||||
|
||||
@ -147,32 +90,33 @@ class Vcpu_handler_vmx : public Vcpu_handler
|
||||
register_handler<VMX_EXIT_TRIPLE_FAULT, This,
|
||||
&This::_vmx_triple> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
register_handler<VMX_EXIT_INIT_SIGNAL, This,
|
||||
&This::_vmx_init> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
&This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
register_handler<VMX_EXIT_IRQ_WINDOW, This,
|
||||
&This::_vmx_irqwin> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
register_handler<VMX_EXIT_CPUID, This,
|
||||
&This::_vmx_cpuid> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
&This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
register_handler<VMX_EXIT_HLT, This,
|
||||
&This::_vmx_hlt> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
&This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
register_handler<VMX_EXIT_RDTSC, This,
|
||||
&This::_vmx_rdtsc> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
&This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
register_handler<VMX_EXIT_VMCALL, This,
|
||||
&This::_vmx_vmcall> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
&This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
register_handler<VMX_EXIT_PORT_IO, This,
|
||||
&This::_vmx_ioio> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
&This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
register_handler<VMX_EXIT_RDMSR, This,
|
||||
&This::_vmx_msr_read> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
&This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
register_handler<VMX_EXIT_WRMSR, This,
|
||||
&This::_vmx_msr_write> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
&This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
register_handler<VMX_EXIT_ERR_INVALID_GUEST_STATE, This,
|
||||
&This::_vmx_invalid> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
&This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
register_handler<VMX_EXIT_PAUSE, This,
|
||||
&This::_vmx_pause> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
&This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
register_handler<VMX_EXIT_EPT_VIOLATION, This,
|
||||
&This::_vmx_ept<VMX_EXIT_EPT_VIOLATION>> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
register_handler<VCPU_STARTUP, This, &This::_vmx_startup>
|
||||
(exc_base, Mtd::ALL | Mtd::FPU);
|
||||
register_handler<RECALL, This, &This::_vmx_recall> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
register_handler<VCPU_STARTUP, This,
|
||||
&This::_vmx_startup> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
register_handler<RECALL, This,
|
||||
&This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
|
||||
|
||||
start();
|
||||
}
|
||||
|
@ -28,6 +28,14 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
/**
|
||||
* Returns true if a vCPU could be started. If false we run without
|
||||
* hardware acceleration support.
|
||||
*/
|
||||
bool create_emt_vcpu(pthread_t * pthread, size_t stack,
|
||||
const pthread_attr_t *attr,
|
||||
void *(*start_routine) (void *), void *arg);
|
||||
|
||||
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||
void *(*start_routine) (void *), void *arg)
|
||||
{
|
||||
@ -45,6 +53,13 @@ extern "C" {
|
||||
"limit to %zu Bytes", rtthread->szName, rtthread->cbStack,
|
||||
stack_size);
|
||||
|
||||
if (rtthread->enmType == RTTHREADTYPE_EMULATION) {
|
||||
|
||||
if (create_emt_vcpu(thread, stack_size, attr, start_routine, arg))
|
||||
return 0;
|
||||
/* no haredware support, create normal pthread thread */
|
||||
}
|
||||
|
||||
pthread_t thread_obj = new (Genode::env()->heap())
|
||||
pthread(attr ? *attr : 0, start_routine,
|
||||
arg, stack_size, rtthread->szName, nullptr);
|
||||
|
Loading…
x
Reference in New Issue
Block a user