Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d686445

Browse files
neelneel
authored andcommitted
Modify handling of writes to the vlapic LVT registers.
The handler is now called after the register value is updated in the virtual APIC page. This will make it easier to handle APIC-write VM-exits with APIC register virtualization turned on. This also implies that we need to keep a snapshot of the last value written to a LVT register. We can no longer rely on the LVT registers in the APIC page to be "clean" because the guest can write anything to it before the hypervisor has had a chance to sanitize it.
1 parent dc8c582 commit d686445

File tree

3 files changed

+131
-58
lines changed

3 files changed

+131
-58
lines changed

sys/amd64/vmm/io/vlapic.c

Lines changed: 113 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ do { \
9494
#define PRIO(x) ((x) >> 4)
9595

9696
#define VLAPIC_VERSION (16)
97-
#define VLAPIC_MAXLVT_ENTRIES (APIC_LVT_CMCI)
9897

9998
#define x2apic(vlapic) (((vlapic)->msr_apicbase & APICBASE_X2APIC) ? 1 : 0)
10099

@@ -212,20 +211,6 @@ vlapic_timer_divisor(uint32_t dcr)
212211
}
213212
}
214213

215-
static void
216-
vlapic_mask_lvts(struct vlapic *vlapic)
217-
{
218-
struct LAPIC *lapic = vlapic->apic_page;
219-
220-
lapic->lvt_cmci |= APIC_LVT_M;
221-
lapic->lvt_timer |= APIC_LVT_M;
222-
lapic->lvt_thermal |= APIC_LVT_M;
223-
lapic->lvt_pcint |= APIC_LVT_M;
224-
lapic->lvt_lint0 |= APIC_LVT_M;
225-
lapic->lvt_lint1 |= APIC_LVT_M;
226-
lapic->lvt_error |= APIC_LVT_M;
227-
}
228-
229214
#if 0
230215
static inline void
231216
vlapic_dump_lvt(uint32_t offset, uint32_t *lvt)
@@ -304,32 +289,6 @@ vlapic_esr_write_handler(struct vlapic *vlapic)
304289
vlapic->esr_pending = 0;
305290
}
306291

307-
static void
308-
vlapic_reset(struct vlapic *vlapic)
309-
{
310-
struct LAPIC *lapic;
311-
312-
lapic = vlapic->apic_page;
313-
bzero(lapic, sizeof(struct LAPIC));
314-
315-
lapic->id = vlapic_get_id(vlapic);
316-
lapic->version = VLAPIC_VERSION;
317-
lapic->version |= (VLAPIC_MAXLVT_ENTRIES << MAXLVTSHIFT);
318-
lapic->dfr = 0xffffffff;
319-
lapic->svr = APIC_SVR_VECTOR;
320-
vlapic_mask_lvts(vlapic);
321-
322-
lapic->dcr_timer = 0;
323-
vlapic_dcr_write_handler(vlapic);
324-
325-
if (vlapic->vcpuid == 0)
326-
vlapic->boot_state = BS_RUNNING; /* BSP */
327-
else
328-
vlapic->boot_state = BS_INIT; /* AP */
329-
330-
vlapic->svr_last = lapic->svr;
331-
}
332-
333292
void
334293
vlapic_set_intr_ready(struct vlapic *vlapic, int vector, bool level)
335294
{
@@ -388,24 +347,65 @@ vlapic_get_lvtptr(struct vlapic *vlapic, uint32_t offset)
388347
}
389348
}
390349

350+
static __inline int
351+
lvt_off_to_idx(uint32_t offset)
352+
{
353+
int index;
354+
355+
switch (offset) {
356+
case APIC_OFFSET_CMCI_LVT:
357+
index = APIC_LVT_CMCI;
358+
break;
359+
case APIC_OFFSET_TIMER_LVT:
360+
index = APIC_LVT_TIMER;
361+
break;
362+
case APIC_OFFSET_THERM_LVT:
363+
index = APIC_LVT_THERMAL;
364+
break;
365+
case APIC_OFFSET_PERF_LVT:
366+
index = APIC_LVT_PMC;
367+
break;
368+
case APIC_OFFSET_LINT0_LVT:
369+
index = APIC_LVT_LINT0;
370+
break;
371+
case APIC_OFFSET_LINT1_LVT:
372+
index = APIC_LVT_LINT1;
373+
break;
374+
case APIC_OFFSET_ERROR_LVT:
375+
index = APIC_LVT_ERROR;
376+
break;
377+
default:
378+
index = -1;
379+
break;
380+
}
381+
KASSERT(index >= 0 && index <= VLAPIC_MAXLVT_INDEX, ("lvt_off_to_idx: "
382+
"invalid lvt index %d for offset %#x", index, offset));
383+
384+
return (index);
385+
}
386+
391387
static __inline uint32_t
392388
vlapic_get_lvt(struct vlapic *vlapic, uint32_t offset)
393389
{
390+
int idx;
391+
uint32_t val;
394392

395-
return (*vlapic_get_lvtptr(vlapic, offset));
393+
idx = lvt_off_to_idx(offset);
394+
val = atomic_load_acq_32(&vlapic->lvt_last[idx]);
395+
return (val);
396396
}
397397

398-
static void
399-
vlapic_set_lvt(struct vlapic *vlapic, uint32_t offset, uint32_t val)
398+
void
399+
vlapic_lvt_write_handler(struct vlapic *vlapic, uint32_t offset)
400400
{
401-
uint32_t *lvtptr, mask;
401+
uint32_t *lvtptr, mask, val;
402402
struct LAPIC *lapic;
403+
int idx;
403404

404405
lapic = vlapic->apic_page;
405406
lvtptr = vlapic_get_lvtptr(vlapic, offset);
406-
407-
if (offset == APIC_OFFSET_TIMER_LVT)
408-
VLAPIC_TIMER_LOCK(vlapic);
407+
val = *lvtptr;
408+
idx = lvt_off_to_idx(offset);
409409

410410
if (!(lapic->svr & APIC_SVR_ENABLE))
411411
val |= APIC_LVT_M;
@@ -424,10 +424,36 @@ vlapic_set_lvt(struct vlapic *vlapic, uint32_t offset, uint32_t val)
424424
mask |= APIC_LVT_DM;
425425
break;
426426
}
427-
*lvtptr = val & mask;
427+
val &= mask;
428+
*lvtptr = val;
429+
atomic_store_rel_32(&vlapic->lvt_last[idx], val);
430+
}
431+
432+
static void
433+
vlapic_mask_lvts(struct vlapic *vlapic)
434+
{
435+
struct LAPIC *lapic = vlapic->apic_page;
436+
437+
lapic->lvt_cmci |= APIC_LVT_M;
438+
vlapic_lvt_write_handler(vlapic, APIC_OFFSET_CMCI_LVT);
439+
440+
lapic->lvt_timer |= APIC_LVT_M;
441+
vlapic_lvt_write_handler(vlapic, APIC_OFFSET_TIMER_LVT);
442+
443+
lapic->lvt_thermal |= APIC_LVT_M;
444+
vlapic_lvt_write_handler(vlapic, APIC_OFFSET_THERM_LVT);
445+
446+
lapic->lvt_pcint |= APIC_LVT_M;
447+
vlapic_lvt_write_handler(vlapic, APIC_OFFSET_PERF_LVT);
448+
449+
lapic->lvt_lint0 |= APIC_LVT_M;
450+
vlapic_lvt_write_handler(vlapic, APIC_OFFSET_LINT0_LVT);
451+
452+
lapic->lvt_lint1 |= APIC_LVT_M;
453+
vlapic_lvt_write_handler(vlapic, APIC_OFFSET_LINT1_LVT);
428454

429-
if (offset == APIC_OFFSET_TIMER_LVT)
430-
VLAPIC_TIMER_UNLOCK(vlapic);
455+
lapic->lvt_error |= APIC_LVT_M;
456+
vlapic_lvt_write_handler(vlapic, APIC_OFFSET_ERROR_LVT);
431457
}
432458

433459
static int
@@ -648,7 +674,7 @@ vlapic_fire_cmci(struct vlapic *vlapic)
648674
}
649675
}
650676

651-
static VMM_STAT_ARRAY(LVTS_TRIGGERRED, VLAPIC_MAXLVT_ENTRIES,
677+
static VMM_STAT_ARRAY(LVTS_TRIGGERRED, VLAPIC_MAXLVT_INDEX + 1,
652678
"lvts triggered");
653679

654680
int
@@ -1166,6 +1192,11 @@ vlapic_read(struct vlapic *vlapic, uint64_t offset, uint64_t *data, bool *retu)
11661192
case APIC_OFFSET_CMCI_LVT:
11671193
case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT:
11681194
*data = vlapic_get_lvt(vlapic, offset);
1195+
#ifdef INVARIANTS
1196+
reg = vlapic_get_lvtptr(vlapic, offset);
1197+
KASSERT(*data == *reg, ("inconsistent lvt value at "
1198+
"offset %#lx: %#lx/%#x", offset, *data, *reg));
1199+
#endif
11691200
break;
11701201
case APIC_OFFSET_TIMER_ICR:
11711202
*data = lapic->icr_timer;
@@ -1190,6 +1221,7 @@ int
11901221
vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu)
11911222
{
11921223
struct LAPIC *lapic = vlapic->apic_page;
1224+
uint32_t *regptr;
11931225
int retval;
11941226

11951227
KASSERT((offset & 0xf) == 0 && offset < PAGE_SIZE,
@@ -1238,7 +1270,9 @@ vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu)
12381270
break;
12391271
case APIC_OFFSET_CMCI_LVT:
12401272
case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT:
1241-
vlapic_set_lvt(vlapic, offset, data);
1273+
regptr = vlapic_get_lvtptr(vlapic, offset);
1274+
*regptr = data;
1275+
vlapic_lvt_write_handler(vlapic, offset);
12421276
break;
12431277
case APIC_OFFSET_TIMER_ICR:
12441278
lapic->icr_timer = data;
@@ -1269,6 +1303,32 @@ vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu)
12691303
return (retval);
12701304
}
12711305

1306+
static void
1307+
vlapic_reset(struct vlapic *vlapic)
1308+
{
1309+
struct LAPIC *lapic;
1310+
1311+
lapic = vlapic->apic_page;
1312+
bzero(lapic, sizeof(struct LAPIC));
1313+
1314+
lapic->id = vlapic_get_id(vlapic);
1315+
lapic->version = VLAPIC_VERSION;
1316+
lapic->version |= (VLAPIC_MAXLVT_INDEX << MAXLVTSHIFT);
1317+
lapic->dfr = 0xffffffff;
1318+
lapic->svr = APIC_SVR_VECTOR;
1319+
vlapic_mask_lvts(vlapic);
1320+
1321+
lapic->dcr_timer = 0;
1322+
vlapic_dcr_write_handler(vlapic);
1323+
1324+
if (vlapic->vcpuid == 0)
1325+
vlapic->boot_state = BS_RUNNING; /* BSP */
1326+
else
1327+
vlapic->boot_state = BS_INIT; /* AP */
1328+
1329+
vlapic->svr_last = lapic->svr;
1330+
}
1331+
12721332
void
12731333
vlapic_init(struct vlapic *vlapic)
12741334
{

sys/amd64/vmm/io/vlapic.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,5 @@ void vlapic_esr_write_handler(struct vlapic *vlapic);
8080
int vlapic_icrlo_write_handler(struct vlapic *vlapic, bool *retu);
8181
void vlapic_icrtmr_write_handler(struct vlapic *vlapic);
8282
void vlapic_dcr_write_handler(struct vlapic *vlapic);
83+
void vlapic_lvt_write_handler(struct vlapic *vlapic, uint32_t offset);
8384
#endif /* _VLAPIC_H_ */

sys/amd64/vmm/io/vlapic_priv.h

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

32+
#include <x86/apicreg.h>
33+
3234
/*
3335
* APIC Register: Offset Description
3436
*/
@@ -91,6 +93,8 @@ enum boot_state {
9193
*/
9294
#define ISRVEC_STK_SIZE (16 + 1)
9395

96+
#define VLAPIC_MAXLVT_INDEX APIC_LVT_CMCI
97+
9498
struct vlapic {
9599
struct vm *vm;
96100
int vcpuid;
@@ -111,12 +115,20 @@ struct vlapic {
111115
* The vector on the top of the stack is used to compute the
112116
* Processor Priority in conjunction with the TPR.
113117
*/
114-
uint8_t isrvec_stk[ISRVEC_STK_SIZE];
115-
int isrvec_stk_top;
118+
uint8_t isrvec_stk[ISRVEC_STK_SIZE];
119+
int isrvec_stk_top;
120+
121+
uint64_t msr_apicbase;
122+
enum boot_state boot_state;
116123

117-
uint64_t msr_apicbase;
118-
enum boot_state boot_state;
119-
uint32_t svr_last;
124+
/*
125+
* Copies of some registers in the virtual APIC page. We do this for
126+
* a couple of different reasons:
127+
* - to be able to detect what changed (e.g. svr_last)
128+
* - to maintain a coherent snapshot of the register (e.g. lvt_last)
129+
*/
130+
uint32_t svr_last;
131+
uint32_t lvt_last[VLAPIC_MAXLVT_INDEX + 1];
120132
};
121133

122134
void vlapic_init(struct vlapic *vlapic);

0 commit comments

Comments
 (0)