Skip to content

Commit 638d714

Browse files
junjiemao1lijinxia
authored andcommitted
DM: adapt to the new VHM request state transitions
This is the counterpart in DM to the VHM request state update in the hypervisor. Major changes include: * Remove accesses to the obsolete 'valid' member. * Access the 'processed' member using atomic operations. * Sync the documentation on vhm_request. In addition, the new state transition also requires a VHM request to be always handled properly, as there is no 'FAILED' state any more. Instead of crashing the device model (and thus the UOS as well), the device model should return all 1s or ignore the request when it is to load from or store to an invalid address, respectively. Note: there is an issue in vm_system_reset() and vm_suspend_resume() where completed VHM requests are not properly notified, causing the hypervisor to complain as it sees uncompleted requests while trying to create a new one. This issue will be resolved in a separate patch. v1 -> v2: * Use macro-defined constants for the default values for invalid PIO/MMIO reads. * Change the return type of vmexit_handler_t in DM to void as the return values are no longer necessary. * Remove VM_EXITCODE that are no longer used. Tracked-On: #875 Signed-off-by: Junjie Mao <junjie.mao@intel.com> Acked-by: Yu Wang <yu1.wang@intel.com>
1 parent ea13758 commit 638d714

File tree

4 files changed

+129
-111
lines changed

4 files changed

+129
-111
lines changed

devicemodel/core/main.c

Lines changed: 26 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,15 @@
6060
#include "monitor.h"
6161
#include "ioc.h"
6262
#include "pm.h"
63+
#include "atomic.h"
6364

6465
#define GUEST_NIO_PORT 0x488 /* guest upcalls via i/o port */
6566

66-
typedef int (*vmexit_handler_t)(struct vmctx *,
67+
/* Values returned for reads on invalid I/O requests. */
68+
#define VHM_REQ_PIO_INVAL (~0U)
69+
#define VHM_REQ_MMIO_INVAL (~0UL)
70+
71+
typedef void (*vmexit_handler_t)(struct vmctx *,
6772
struct vhm_request *, int *vcpu);
6873

6974
char *vmname;
@@ -287,7 +292,7 @@ delete_cpu(struct vmctx *ctx, int vcpu)
287292
return CPU_EMPTY(&cpumask);
288293
}
289294

290-
static int
295+
static void
291296
vmexit_inout(struct vmctx *ctx, struct vhm_request *vhm_req, int *pvcpu)
292297
{
293298
int error;
@@ -303,13 +308,14 @@ vmexit_inout(struct vmctx *ctx, struct vhm_request *vhm_req, int *pvcpu)
303308
in ? "in" : "out",
304309
bytes == 1 ? 'b' : (bytes == 2 ? 'w' : 'l'),
305310
port);
306-
return VMEXIT_ABORT;
307-
} else {
308-
return VMEXIT_CONTINUE;
311+
312+
if (in) {
313+
vhm_req->reqs.pio_request.value = VHM_REQ_PIO_INVAL;
314+
}
309315
}
310316
}
311317

312-
static int
318+
static void
313319
vmexit_mmio_emul(struct vmctx *ctx, struct vhm_request *vhm_req, int *pvcpu)
314320
{
315321
int err;
@@ -326,14 +332,14 @@ vmexit_mmio_emul(struct vmctx *ctx, struct vhm_request *vhm_req, int *pvcpu)
326332
fprintf(stderr, "mmio address 0x%lx, size %ld",
327333
vhm_req->reqs.mmio_request.address,
328334
vhm_req->reqs.mmio_request.size);
329-
vhm_req->processed = REQ_STATE_FAILED;
330-
return VMEXIT_ABORT;
335+
336+
if (vhm_req->reqs.mmio_request.direction == REQUEST_READ) {
337+
vhm_req->reqs.mmio_request.value = VHM_REQ_MMIO_INVAL;
338+
}
331339
}
332-
vhm_req->processed = REQ_STATE_SUCCESS;
333-
return VMEXIT_CONTINUE;
334340
}
335341

336-
static int
342+
static void
337343
vmexit_pci_emul(struct vmctx *ctx, struct vhm_request *vhm_req, int *pvcpu)
338344
{
339345
int err, in = (vhm_req->reqs.pci_request.direction == REQUEST_READ);
@@ -351,11 +357,11 @@ vmexit_pci_emul(struct vmctx *ctx, struct vhm_request *vhm_req, int *pvcpu)
351357
vhm_req->reqs.pci_request.dev,
352358
vhm_req->reqs.pci_request.func,
353359
vhm_req->reqs.pci_request.reg);
354-
return VMEXIT_ABORT;
355-
}
356360

357-
vhm_req->processed = REQ_STATE_SUCCESS;
358-
return VMEXIT_CONTINUE;
361+
if (in) {
362+
vhm_req->reqs.pio_request.value = VHM_REQ_PIO_INVAL;
363+
}
364+
}
359365
}
360366

361367
#define DEBUG_EPT_MISCONFIG
@@ -368,66 +374,15 @@ vmexit_pci_emul(struct vmctx *ctx, struct vhm_request *vhm_req, int *pvcpu)
368374

369375
#endif /* #ifdef DEBUG_EPT_MISCONFIG */
370376

371-
static int
372-
vmexit_bogus(struct vmctx *ctx, struct vhm_request *vhm_req, int *pvcpu)
373-
{
374-
stats.vmexit_bogus++;
375-
376-
return VMEXIT_CONTINUE;
377-
}
378-
379-
static int
380-
vmexit_reqidle(struct vmctx *ctx, struct vhm_request *vhm_req, int *pvcpu)
381-
{
382-
stats.vmexit_reqidle++;
383-
384-
return VMEXIT_CONTINUE;
385-
}
386-
387-
static int
388-
vmexit_hlt(struct vmctx *ctx, struct vhm_request *vhm_req, int *pvcpu)
389-
{
390-
stats.vmexit_hlt++;
391-
392-
/*
393-
* Just continue execution with the next instruction. We use
394-
* the HLT VM exit as a way to be friendly with the host
395-
* scheduler.
396-
*/
397-
return VMEXIT_CONTINUE;
398-
}
399-
400-
static int
401-
vmexit_pause(struct vmctx *ctx, struct vhm_request *vhm_req, int *pvcpu)
402-
{
403-
stats.vmexit_pause++;
404-
405-
return VMEXIT_CONTINUE;
406-
}
407-
408-
static int
409-
vmexit_mtrap(struct vmctx *ctx, struct vhm_request *vhm_req, int *pvcpu)
410-
{
411-
stats.vmexit_mtrap++;
412-
413-
return VMEXIT_CONTINUE;
414-
}
415-
416377
static vmexit_handler_t handler[VM_EXITCODE_MAX] = {
417378
[VM_EXITCODE_INOUT] = vmexit_inout,
418379
[VM_EXITCODE_MMIO_EMUL] = vmexit_mmio_emul,
419380
[VM_EXITCODE_PCI_CFG] = vmexit_pci_emul,
420-
[VM_EXITCODE_BOGUS] = vmexit_bogus,
421-
[VM_EXITCODE_REQIDLE] = vmexit_reqidle,
422-
[VM_EXITCODE_MTRAP] = vmexit_mtrap,
423-
[VM_EXITCODE_HLT] = vmexit_hlt,
424-
[VM_EXITCODE_PAUSE] = vmexit_pause,
425381
};
426382

427383
static void
428384
handle_vmexit(struct vmctx *ctx, struct vhm_request *vhm_req, int vcpu)
429385
{
430-
int rc;
431386
enum vm_exitcode exitcode;
432387

433388
exitcode = vhm_req->type;
@@ -437,17 +392,8 @@ handle_vmexit(struct vmctx *ctx, struct vhm_request *vhm_req, int vcpu)
437392
exit(1);
438393
}
439394

440-
rc = (*handler[exitcode])(ctx, vhm_req, &vcpu);
441-
switch (rc) {
442-
case VMEXIT_CONTINUE:
443-
vhm_req->processed = REQ_STATE_SUCCESS;
444-
break;
445-
case VMEXIT_ABORT:
446-
vhm_req->processed = REQ_STATE_FAILED;
447-
abort();
448-
default:
449-
exit(1);
450-
}
395+
(*handler[exitcode])(ctx, vhm_req, &vcpu);
396+
atomic_store(&vhm_req->processed, REQ_STATE_COMPLETE);
451397

452398
/* If UOS is not in suspend or system reset mode, we don't
453399
* need to notify request done.
@@ -580,8 +526,7 @@ vm_system_reset(struct vmctx *ctx)
580526
struct vhm_request *vhm_req;
581527

582528
vhm_req = &vhm_req_buf[vcpu_id];
583-
if (vhm_req->valid &&
584-
(vhm_req->processed == REQ_STATE_PROCESSING) &&
529+
if ((atomic_load(&vhm_req->processed) == REQ_STATE_PROCESSING) &&
585530
(vhm_req->client == ctx->ioreq_client))
586531
vm_notify_request_done(ctx, vcpu_id);
587532
}
@@ -613,8 +558,7 @@ vm_suspend_resume(struct vmctx *ctx)
613558
struct vhm_request *vhm_req;
614559

615560
vhm_req = &vhm_req_buf[vcpu_id];
616-
if (vhm_req->valid &&
617-
(vhm_req->processed == REQ_STATE_PROCESSING) &&
561+
if ((atomic_load(&vhm_req->processed) == REQ_STATE_PROCESSING) &&
618562
(vhm_req->client == ctx->ioreq_client))
619563
vm_notify_request_done(ctx, vcpu_id);
620564
}
@@ -648,8 +592,7 @@ vm_loop(struct vmctx *ctx)
648592

649593
for (vcpu_id = 0; vcpu_id < 4; vcpu_id++) {
650594
vhm_req = &vhm_req_buf[vcpu_id];
651-
if (vhm_req->valid
652-
&& (vhm_req->processed == REQ_STATE_PROCESSING)
595+
if ((atomic_load(&vhm_req->processed) == REQ_STATE_PROCESSING)
653596
&& (vhm_req->client == ctx->ioreq_client))
654597
handle_vmexit(ctx, vhm_req, vcpu_id);
655598
}

devicemodel/include/dm.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@
2929
#ifndef _DM_H_
3030
#define _DM_H_
3131

32-
#define VMEXIT_CONTINUE (0)
33-
#define VMEXIT_ABORT (-1)
3432
#include <stdbool.h>
3533
#include "types.h"
3634
#include "vmm.h"

devicemodel/include/public/acrn_common.h

Lines changed: 103 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@
4646
#define VHM_REQUEST_MAX 16U
4747

4848
#define REQ_STATE_PENDING 0
49-
#define REQ_STATE_SUCCESS 1
49+
#define REQ_STATE_COMPLETE 1
5050
#define REQ_STATE_PROCESSING 2
51-
#define REQ_STATE_FAILED -1
51+
#define REQ_STATE_FREE 3
5252

5353
#define REQ_PORTIO 0U
5454
#define REQ_MMIO 1U
@@ -102,32 +102,121 @@ struct pci_request {
102102
int32_t reg;
103103
} __aligned(8);
104104

105-
/* vhm_request are 256Bytes aligned */
105+
/**
106+
* @brief 256-byte VHM requests
107+
*
108+
* The state transitions of a VHM request are:
109+
*
110+
* FREE -> PENDING -> PROCESSING -> COMPLETE -> FREE -> ...
111+
*
112+
* When a request is in COMPLETE or FREE state, the request is owned by the
113+
* hypervisor. SOS (VHM or DM) shall not read or write the internals of the
114+
* request except the state.
115+
*
116+
* When a request is in PENDING or PROCESSING state, the request is owned by
117+
* SOS. The hypervisor shall not read or write the request other than the state.
118+
*
119+
* Based on the rules above, a typical VHM request lifecycle should looks like
120+
* the following.
121+
*
122+
* (assume the initial state is FREE)
123+
*
124+
* SOS vCPU 0 SOS vCPU x UOS vCPU y
125+
*
126+
* hypervisor:
127+
* fill in type, addr, etc.
128+
* pause UOS vcpu y
129+
* set state to PENDING (a)
130+
* fire upcall to SOS vCPU 0
131+
*
132+
* VHM:
133+
* scan for pending requests
134+
* set state to PROCESSING (b)
135+
* assign requests to clients (c)
136+
*
137+
* client:
138+
* scan for assigned requests
139+
* handle the requests (d)
140+
* set state to COMPLETE
141+
* notify the hypervisor
142+
*
143+
* hypervisor:
144+
* resume UOS vcpu y (e)
145+
*
146+
* hypervisor:
147+
* post-work (f)
148+
* set state to FREE
149+
*
150+
* Note that the following shall hold.
151+
*
152+
* 1. (a) happens before (b)
153+
* 2. (c) happens before (d)
154+
* 3. (e) happens before (f)
155+
* 4. One vCPU cannot trigger another I/O request before the previous one has
156+
* completed (i.e. the state switched to FREE)
157+
*
158+
* Accesses to the state of a vhm_request shall be atomic and proper barriers
159+
* are needed to ensure that:
160+
*
161+
* 1. Setting state to PENDING is the last operation when issuing a request in
162+
* the hypervisor, as the hypervisor shall not access the request any more.
163+
*
164+
* 2. Due to similar reasons, setting state to COMPLETE is the last operation
165+
* of request handling in VHM or clients in SOS.
166+
*/
106167
struct vhm_request {
107-
/* offset: 0bytes - 63bytes */
168+
/**
169+
* @brief Type of this request.
170+
*
171+
* Byte offset: 0.
172+
*/
108173
uint32_t type;
109-
int32_t reserved0[15];
110174

111-
/* offset: 64bytes-127bytes */
175+
/**
176+
* @brief Reserved.
177+
*
178+
* Byte offset: 4.
179+
*/
180+
uint32_t reserved0[15];
181+
182+
/**
183+
* @brief Details about this request.
184+
*
185+
* For REQ_PORTIO, this has type pio_request. For REQ_MMIO and REQ_WP,
186+
* this has type mmio_request. For REQ_PCICFG, this has type
187+
* pci_request.
188+
*
189+
* Byte offset: 64.
190+
*/
112191
union {
113192
struct pio_request pio_request;
114193
struct pci_request pci_request;
115194
struct mmio_request mmio_request;
116195
int64_t reserved1[8];
117196
} reqs;
118197

119-
/* True: valid req which need VHM to process.
120-
* ACRN write, VHM read only
198+
/**
199+
* @brief Reserved.
200+
*
201+
* Byte offset: 132.
121202
**/
122-
int32_t valid;
203+
uint32_t reserved1;
123204

124-
/* the client which is distributed to handle this request */
205+
/**
206+
* @brief The client which is distributed to handle this request.
207+
*
208+
* Accessed by VHM only.
209+
*
210+
* Byte offset: 132.
211+
*/
125212
int32_t client;
126213

127-
/* 1: VHM had processed and success
128-
* 0: VHM had not yet processed
129-
* -1: VHM failed to process. Invalid request
130-
* VHM write, ACRN read only
214+
/**
215+
* @brief The status of this request.
216+
*
217+
* Taking REQ_STATE_xxx as values.
218+
*
219+
* Byte offset: 136.
131220
*/
132221
int32_t processed;
133222
} __aligned(256);

devicemodel/include/vmm.h

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -237,18 +237,6 @@ enum vm_exitcode {
237237
VM_EXITCODE_INOUT = 0,
238238
VM_EXITCODE_MMIO_EMUL,
239239
VM_EXITCODE_PCI_CFG,
240-
VM_EXITCODE_BOGUS,
241-
VM_EXITCODE_HLT,
242-
VM_EXITCODE_MTRAP,
243-
VM_EXITCODE_PAUSE,
244-
VM_EXITCODE_PAGING,
245-
VM_EXITCODE_DEPRECATED1, /* used to be SPINDOWN_CPU */
246-
VM_EXITCODE_RENDEZVOUS,
247-
VM_EXITCODE_IOAPIC_EOI,
248-
VM_EXITCODE_INOUT_STR,
249-
VM_EXITCODE_MONITOR,
250-
VM_EXITCODE_MWAIT,
251-
VM_EXITCODE_REQIDLE,
252240
VM_EXITCODE_MAX
253241
};
254242

0 commit comments

Comments
 (0)