Skip to content

Commit 8787b65

Browse files
fyin1wenlingz
authored andcommitted
dm: fix the issue when guest tries to disable memory range access
According to PCI spec 3.0 section 6.2.2 "Device Control", guest could write the command register to control device response to io/mem access. The origial code register/unregister the memory range which is not suitable because it can't handle the sequence: 1. disble the device response to specific memory range 2. reboot guest (DM will try to free the memory range which was freed in step 1 already) Tracked-On: #1277 Signed-off-by: Yin Fengwei <fengwei.yin@intel.com> Acked-by: Yu Wang <yu1.wang@intel.com>
1 parent be0cde7 commit 8787b65

File tree

5 files changed

+182
-4
lines changed

5 files changed

+182
-4
lines changed

devicemodel/core/inout.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* $FreeBSD$
2727
*/
2828

29+
#include <stdbool.h>
2930
#include <stdio.h>
3031
#include <string.h>
3132
#include <assert.h>
@@ -44,6 +45,7 @@ static struct {
4445
int flags;
4546
inout_func_t handler;
4647
void *arg;
48+
bool enabled;
4749
} inout_handlers[MAX_IOPORTS];
4850

4951
static int
@@ -113,6 +115,11 @@ emulate_inout(struct vmctx *ctx, int *pvcpu, struct pio_request *pio_request)
113115
if (!(flags & IOPORT_F_OUT))
114116
return -1;
115117
}
118+
119+
if (inout_handlers[port].enabled == false) {
120+
return -1;
121+
}
122+
116123
retval = handler(ctx, *pvcpu, in, port, bytes,
117124
(uint32_t *)&(pio_request->value), arg);
118125
return retval;
@@ -141,6 +148,42 @@ init_inout(void)
141148
}
142149
}
143150

151+
int
152+
disable_inout(struct inout_port *iop)
153+
{
154+
int i;
155+
156+
if (!VERIFY_IOPORT(iop->port, iop->size)) {
157+
printf("invalid input: port:0x%x, size:%d",
158+
iop->port, iop->size);
159+
return -1;
160+
}
161+
162+
for (i = iop->port; i < iop->port + iop->size; i++) {
163+
inout_handlers[i].enabled = false;
164+
}
165+
166+
return 0;
167+
}
168+
169+
int
170+
enable_inout(struct inout_port *iop)
171+
{
172+
int i;
173+
174+
if (!VERIFY_IOPORT(iop->port, iop->size)) {
175+
printf("invalid input: port:0x%x, size:%d",
176+
iop->port, iop->size);
177+
return -1;
178+
}
179+
180+
for (i = iop->port; i < iop->port + iop->size; i++) {
181+
inout_handlers[i].enabled = true;
182+
}
183+
184+
return 0;
185+
}
186+
144187
int
145188
register_inout(struct inout_port *iop)
146189
{
@@ -168,6 +211,7 @@ register_inout(struct inout_port *iop)
168211
inout_handlers[i].flags = iop->flags;
169212
inout_handlers[i].handler = iop->handler;
170213
inout_handlers[i].arg = iop->arg;
214+
inout_handlers[i].enabled = true;
171215
}
172216

173217
return 0;

devicemodel/core/mem.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
*/
3434

3535
#include <errno.h>
36+
#include <stdbool.h>
3637
#include <stdio.h>
3738
#include <stdlib.h>
3839
#include <assert.h>
@@ -47,6 +48,7 @@ struct mmio_rb_range {
4748
struct mem_range mr_param;
4849
uint64_t mr_base;
4950
uint64_t mr_end;
51+
bool enabled;
5052
};
5153

5254
struct mmio_rb_tree;
@@ -181,6 +183,10 @@ emulate_mem(struct vmctx *ctx, struct mmio_request *mmio_req)
181183

182184
assert(entry != NULL);
183185

186+
if (entry->enabled == false) {
187+
return -1;
188+
}
189+
184190
if (mmio_req->direction == REQUEST_READ)
185191
err = mem_read(ctx, 0, paddr, (uint64_t *)&mmio_req->value,
186192
size, &entry->mr_param);
@@ -207,6 +213,7 @@ register_mem_int(struct mmio_rb_tree *rbt, struct mem_range *memp)
207213
mrp->mr_param = *memp;
208214
mrp->mr_base = memp->base;
209215
mrp->mr_end = memp->base + memp->size - 1;
216+
mrp->enabled = true;
210217
pthread_rwlock_wrlock(&mmio_rwlock);
211218
if (mmio_rb_lookup(rbt, memp->base, &entry) != 0)
212219
err = mmio_rb_add(rbt, mrp);
@@ -219,6 +226,68 @@ register_mem_int(struct mmio_rb_tree *rbt, struct mem_range *memp)
219226
return err;
220227
}
221228

229+
int
230+
disable_mem(struct mem_range *memp)
231+
{
232+
uint64_t paddr = memp->base;
233+
struct mmio_rb_range *entry = NULL;
234+
235+
pthread_rwlock_rdlock(&mmio_rwlock);
236+
/*
237+
* First check the per-VM cache
238+
*/
239+
if (mmio_hint && paddr >= mmio_hint->mr_base &&
240+
paddr <= mmio_hint->mr_end)
241+
entry = mmio_hint;
242+
243+
if (entry == NULL) {
244+
if (mmio_rb_lookup(&mmio_rb_root, paddr, &entry) == 0)
245+
/* Update the per-VMU cache */
246+
mmio_hint = entry;
247+
else if (mmio_rb_lookup(&mmio_rb_fallback, paddr, &entry)) {
248+
pthread_rwlock_unlock(&mmio_rwlock);
249+
return -ESRCH;
250+
}
251+
}
252+
253+
assert(entry != NULL);
254+
entry->enabled = false;
255+
pthread_rwlock_unlock(&mmio_rwlock);
256+
257+
return 0;
258+
}
259+
260+
int
261+
enable_mem(struct mem_range *memp)
262+
{
263+
uint64_t paddr = memp->base;
264+
struct mmio_rb_range *entry = NULL;
265+
266+
pthread_rwlock_rdlock(&mmio_rwlock);
267+
/*
268+
* First check the per-VM cache
269+
*/
270+
if (mmio_hint && paddr >= mmio_hint->mr_base &&
271+
paddr <= mmio_hint->mr_end)
272+
entry = mmio_hint;
273+
274+
if (entry == NULL) {
275+
if (mmio_rb_lookup(&mmio_rb_root, paddr, &entry) == 0)
276+
/* Update the per-VMU cache */
277+
mmio_hint = entry;
278+
else if (mmio_rb_lookup(&mmio_rb_fallback, paddr, &entry)) {
279+
pthread_rwlock_unlock(&mmio_rwlock);
280+
return -ESRCH;
281+
}
282+
}
283+
284+
assert(entry != NULL);
285+
entry->enabled = true;
286+
pthread_rwlock_unlock(&mmio_rwlock);
287+
288+
return 0;
289+
}
290+
222291
int
223292
register_mem(struct mem_range *memp)
224293
{

devicemodel/hw/pci/core.c

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,66 @@ register_bar(struct pci_vdev *dev, int idx)
508508
modify_bar_registration(dev, idx, 1);
509509
}
510510

511+
static void
512+
enable_bar(struct pci_vdev *dev, int idx)
513+
{
514+
int error = 0;
515+
struct inout_port iop;
516+
struct mem_range mr;
517+
518+
switch (dev->bar[idx].type) {
519+
case PCIBAR_IO:
520+
bzero(&iop, sizeof(struct inout_port));
521+
iop.name = dev->name;
522+
iop.port = dev->bar[idx].addr;
523+
iop.size = dev->bar[idx].size;
524+
error = enable_inout(&iop);
525+
break;
526+
case PCIBAR_MEM32:
527+
case PCIBAR_MEM64:
528+
bzero(&mr, sizeof(struct mem_range));
529+
mr.name = dev->name;
530+
mr.base = dev->bar[idx].addr;
531+
mr.size = dev->bar[idx].size;
532+
error = enable_mem(&mr);
533+
break;
534+
default:
535+
error = EINVAL;
536+
break;
537+
}
538+
assert(error == 0);
539+
}
540+
541+
static void
542+
disable_bar(struct pci_vdev *dev, int idx)
543+
{
544+
int error = 0;
545+
struct inout_port iop;
546+
struct mem_range mr;
547+
548+
switch (dev->bar[idx].type) {
549+
case PCIBAR_IO:
550+
bzero(&iop, sizeof(struct inout_port));
551+
iop.name = dev->name;
552+
iop.port = dev->bar[idx].addr;
553+
iop.size = dev->bar[idx].size;
554+
error = disable_inout(&iop);
555+
break;
556+
case PCIBAR_MEM32:
557+
case PCIBAR_MEM64:
558+
bzero(&mr, sizeof(struct mem_range));
559+
mr.name = dev->name;
560+
mr.base = dev->bar[idx].addr;
561+
mr.size = dev->bar[idx].size;
562+
error = disable_mem(&mr);
563+
break;
564+
default:
565+
error = EINVAL;
566+
break;
567+
}
568+
assert(error == 0);
569+
}
570+
511571
/* Are we decoding i/o port accesses for the emulated pci device? */
512572
static int
513573
porten(struct pci_vdev *dev)
@@ -1875,19 +1935,19 @@ pci_emul_cmdsts_write(struct pci_vdev *dev, int coff, uint32_t new, int bytes)
18751935
/* I/O address space decoding changed? */
18761936
if (changed & PCIM_CMD_PORTEN) {
18771937
if (porten(dev))
1878-
register_bar(dev, i);
1938+
enable_bar(dev, i);
18791939
else
1880-
unregister_bar(dev, i);
1940+
disable_bar(dev, i);
18811941
}
18821942
break;
18831943
case PCIBAR_MEM32:
18841944
case PCIBAR_MEM64:
18851945
/* MMIO address space decoding changed? */
18861946
if (changed & PCIM_CMD_MEMEN) {
18871947
if (memen(dev))
1888-
register_bar(dev, i);
1948+
enable_bar(dev, i);
18891949
else
1890-
unregister_bar(dev, i);
1950+
disable_bar(dev, i);
18911951
}
18921952
break;
18931953
default:

devicemodel/include/inout.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ void init_inout(void);
7474
int emulate_inout(struct vmctx *ctx, int *pvcpu, struct pio_request *req);
7575
int register_inout(struct inout_port *iop);
7676
int unregister_inout(struct inout_port *iop);
77+
int enable_inout(struct inout_port *iop);
78+
int disable_inout(struct inout_port *iop);
7779
int init_bvmcons(void);
7880
void deinit_bvmcons(void);
7981
void enable_bvmcons(void);

devicemodel/include/mem.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ struct mem_range {
4242
long arg2;
4343
uint64_t base;
4444
uint64_t size;
45+
bool enabled;
4546
};
4647
#define MEM_F_READ 0x1
4748
#define MEM_F_WRITE 0x2
@@ -54,5 +55,7 @@ int register_mem(struct mem_range *memp);
5455
int register_mem_fallback(struct mem_range *memp);
5556
int unregister_mem(struct mem_range *memp);
5657
int unregister_mem_fallback(struct mem_range *memp);
58+
int disable_mem(struct mem_range *memp);
59+
int enable_mem(struct mem_range *memp);
5760

5861
#endif /* _MEM_H_ */

0 commit comments

Comments
 (0)