acrn-hypervisor/hypervisor/common/io_request.c

195 lines
4.7 KiB
C

/*
* Copyright (C) 2018 Intel Corporation.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <hypervisor.h>
#include <acrn_common.h>
#include <hv_lib.h>
#include <hv_arch.h>
#include <hv_debug.h>
#define ACRN_DBG_IOREQUEST 6
static void fire_vhm_interrupt(void)
{
/*
* use vLAPIC to inject vector to SOS vcpu 0 if vlapic is enabled
* otherwise, send IPI hardcoded to CPU_BOOT_ID
*/
struct vm *vm0;
struct vcpu *vcpu;
vm0 = get_vm_from_vmid(0);
ASSERT(vm0, "VM Pointer is NULL");
vcpu = vcpu_from_vid(vm0, 0);
ASSERT(vcpu, "vcpu_from_vid failed");
vlapic_intr_edge(vcpu, VECTOR_VIRT_IRQ_VHM);
}
static void acrn_print_request(int vcpu_id, struct vhm_request *req)
{
switch (req->type) {
case REQ_MMIO:
dev_dbg(ACRN_DBG_IOREQUEST, "[vcpu_id=%d type=MMIO]", vcpu_id);
dev_dbg(ACRN_DBG_IOREQUEST,
"gpa=0x%lx, R/W=%d, size=%ld value=0x%lx processed=%lx",
req->reqs.mmio_request.address,
req->reqs.mmio_request.direction,
req->reqs.mmio_request.size,
req->reqs.mmio_request.value,
req->processed);
break;
case REQ_PORTIO:
dev_dbg(ACRN_DBG_IOREQUEST, "[vcpu_id=%d type=PORTIO]", vcpu_id);
dev_dbg(ACRN_DBG_IOREQUEST,
"IO=0x%lx, R/W=%d, size=%ld value=0x%lx processed=%lx",
req->reqs.pio_request.address,
req->reqs.pio_request.direction,
req->reqs.pio_request.size,
req->reqs.pio_request.value,
req->processed);
break;
default:
dev_dbg(ACRN_DBG_IOREQUEST, "[vcpu_id=%d type=%d] NOT support type",
vcpu_id, req->type);
break;
}
}
int acrn_insert_request_wait(struct vcpu *vcpu, struct vhm_request *req)
{
struct vhm_request_buffer *req_buf = NULL;
long cur;
ASSERT(sizeof(*req) == (4096/VHM_REQUEST_MAX),
"vhm_request page broken!");
if (!vcpu || !req || vcpu->vm->sw.io_shared_page == NULL)
return -EINVAL;
req_buf = (struct vhm_request_buffer *)(vcpu->vm->sw.io_shared_page);
/* ACRN insert request to VHM and inject upcall */
cur = vcpu->vcpu_id;
memcpy_s(&req_buf->req_queue[cur], sizeof(struct vhm_request),
req, sizeof(struct vhm_request));
/* Must clear the signal before we mark req valid
* Once we mark to valid, VHM may process req and signal us
* before we perform upcall.
* because VHM can work in pulling mode without wait for upcall
*/
req_buf->req_queue[cur].valid = true;
acrn_print_request(vcpu->vcpu_id, req_buf->req_queue + cur);
/* signal VHM */
fire_vhm_interrupt();
/* pause vcpu, wait for VHM to handle the MMIO request */
atomic_store(&vcpu->ioreq_pending, 1);
pause_vcpu(vcpu, VCPU_PAUSED);
return 0;
}
static void _get_req_info_(struct vhm_request *req, int *id, char *type,
char *state, char *dir, long *addr, long *val)
{
strcpy_s(dir, 16, "NONE");
*addr = *val = 0;
*id = req->client;
switch (req->type) {
case REQ_PORTIO:
strcpy_s(type, 16, "PORTIO");
if (req->reqs.pio_request.direction == REQUEST_READ)
strcpy_s(dir, 16, "READ");
else
strcpy_s(dir, 16, "WRITE");
*addr = req->reqs.pio_request.address;
*val = req->reqs.pio_request.value;
break;
case REQ_MMIO:
case REQ_WP:
strcpy_s(type, 16, "MMIO/WP");
if (req->reqs.mmio_request.direction == REQUEST_READ)
strcpy_s(dir, 16, "READ");
else
strcpy_s(dir, 16, "WRITE");
*addr = req->reqs.mmio_request.address;
*val = req->reqs.mmio_request.value;
break;
break;
default:
strcpy_s(type, 16, "UNKNOWN");
}
switch (req->processed) {
case REQ_STATE_SUCCESS:
strcpy_s(state, 16, "SUCCESS");
break;
case REQ_STATE_PENDING:
strcpy_s(state, 16, "PENDING");
break;
case REQ_STATE_PROCESSING:
strcpy_s(state, 16, "PROCESS");
break;
case REQ_STATE_FAILED:
strcpy_s(state, 16, "FAILED");
break;
default:
strcpy_s(state, 16, "UNKNOWN");
}
}
int get_req_info(char *str, int str_max)
{
int i, len, size = str_max, client_id;
struct vhm_request_buffer *req_buf;
struct vhm_request *req;
char type[16], state[16], dir[16];
long addr, val;
struct list_head *pos;
struct vm *vm;
len = snprintf(str, size,
"\r\nVM\tVCPU\tCID\tTYPE\tSTATE\tDIR\tADDR\t\t\tVAL");
size -= len;
str += len;
spinlock_obtain(&vm_list_lock);
list_for_each(pos, &vm_list) {
vm = list_entry(pos, struct vm, list);
req_buf = (struct vhm_request_buffer *)vm->sw.io_shared_page;
if (req_buf) {
for (i = 0; i < VHM_REQUEST_MAX; i++) {
req = req_buf->req_queue + i;
if (req->valid) {
_get_req_info_(req, &client_id, type,
state, dir, &addr, &val);
len = snprintf(str, size,
"\r\n%d\t%d\t%d\t%s\t%s\t%s",
vm->attr.id, i, client_id, type,
state, dir);
size -= len;
str += len;
len = snprintf(str, size,
"\t0x%016llx\t0x%016llx",
addr, val);
size -= len;
str += len;
}
}
}
}
spinlock_release(&vm_list_lock);
snprintf(str, size, "\r\n");
return 0;
}