Changeset View
Standalone View
usr.sbin/bhyve/pci_emul.c
Show All 39 Lines | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <strings.h> | #include <strings.h> | ||||
#include <assert.h> | #include <assert.h> | ||||
#include <stdbool.h> | #include <stdbool.h> | ||||
#include <machine/vmm.h> | #include <machine/vmm.h> | ||||
#include <machine/vmm_snapshot.h> | |||||
#include <vmmapi.h> | #include <vmmapi.h> | ||||
#include "acpi.h" | #include "acpi.h" | ||||
#include "bhyverun.h" | #include "bhyverun.h" | ||||
#include "debug.h" | #include "debug.h" | ||||
#include "inout.h" | #include "inout.h" | ||||
#include "ioapic.h" | #include "ioapic.h" | ||||
#include "mem.h" | #include "mem.h" | ||||
▲ Show 20 Lines • Show All 1,901 Lines • ▼ Show 20 Lines | pci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes, | ||||
return (0); | return (0); | ||||
} | } | ||||
INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata); | INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata); | ||||
INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata); | INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata); | ||||
INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata); | INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata); | ||||
INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata); | INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata); | ||||
#ifdef BHYVE_SNAPSHOT | |||||
/* | |||||
* Saves/restores PCI device emulated state. Returns 0 on success. | |||||
*/ | |||||
static int | |||||
pci_snapshot_pci_dev(struct vm_snapshot_meta *meta) | |||||
{ | |||||
struct pci_devinst *pi; | |||||
int i; | |||||
int ret; | |||||
pi = meta->dev_data; | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.enabled, meta, ret, done); | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.addr, meta, ret, done); | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.msg_data, meta, ret, done); | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.maxmsgnum, meta, ret, done); | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.enabled, meta, ret, done); | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_bar, meta, ret, done); | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_bar, meta, ret, done); | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_offset, meta, ret, done); | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_count, meta, ret, done); | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_offset, meta, ret, done); | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_size, meta, ret, done); | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.function_mask, meta, ret, done); | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_page_offset, meta, ret, done); | |||||
SNAPSHOT_BUF_OR_LEAVE(pi->pi_cfgdata, sizeof(pi->pi_cfgdata), | |||||
meta, ret, done); | |||||
for (i = 0; i < nitems(pi->pi_bar); i++) { | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].type, meta, ret, done); | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].size, meta, ret, done); | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].addr, meta, ret, done); | |||||
} | |||||
/* Restore MSI-X table. */ | |||||
for (i = 0; i < pi->pi_msix.table_count; i++) { | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].addr, | |||||
meta, ret, done); | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].msg_data, | |||||
meta, ret, done); | |||||
SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].vector_control, | |||||
meta, ret, done); | |||||
} | |||||
done: | |||||
return (ret); | |||||
} | |||||
static int | |||||
pci_find_slotted_dev(const char *dev_name, struct pci_devemu **pde, | |||||
struct pci_devinst **pdi) | |||||
{ | |||||
struct businfo *bi; | |||||
struct slotinfo *si; | |||||
struct funcinfo *fi; | |||||
int bus, slot, func; | |||||
assert(dev_name != NULL); | |||||
assert(pde != NULL); | |||||
assert(pdi != NULL); | |||||
for (bus = 0; bus < MAXBUSES; bus++) { | |||||
if ((bi = pci_businfo[bus]) == NULL) | |||||
continue; | |||||
for (slot = 0; slot < MAXSLOTS; slot++) { | |||||
si = &bi->slotinfo[slot]; | |||||
for (func = 0; func < MAXFUNCS; func++) { | |||||
fi = &si->si_funcs[func]; | |||||
if (fi->fi_name == NULL) | |||||
continue; | |||||
if (strcmp(dev_name, fi->fi_name)) | |||||
continue; | |||||
*pde = pci_emul_finddev(fi->fi_name); | |||||
assert(*pde != NULL); | |||||
*pdi = fi->fi_devi; | |||||
return (0); | |||||
} | |||||
} | |||||
} | |||||
return (EINVAL); | |||||
} | |||||
int | |||||
pci_snapshot(struct vm_snapshot_meta *meta) | |||||
{ | |||||
struct pci_devemu *pde; | |||||
struct pci_devinst *pdi; | |||||
int ret; | |||||
assert(meta->dev_name != NULL); | |||||
ret = pci_find_slotted_dev(meta->dev_name, &pde, &pdi); | |||||
if (ret != 0) { | |||||
novel: I get these messages for devices that I don't actually have in a VM, is that expected behavior? | |||||
Not Done Inline ActionsIt's because the devices are in a static array in snapshot.c - that's also why only one device of each type is allowed. When we'll add support for dynamically registering devices, these messages shouldn't appear anymore. The \r\n line endings are put there because the output from bhyve was going to the next line (new line was inserted), but continued from where the last line ended (no carriage return). We didn't find why this happens, and went with the \r\n newlines instead. No real reason to keep them if the output behaves, though. darius.mihaim_gmail.com: It's because the devices are in a static array in `snapshot.c` - that's also why only one… | |||||
Not Done Inline ActionsWhen you are using stdio for the console, it leaves the console in raw mode so \n is only a newline and not a full CR+LF combo. Other printfs in bhyve only use \n though since stdio isn't always used as the console. jhb: When you are using stdio for the console, it leaves the console in raw mode so \n is only a… | |||||
Done Inline ActionsWill keep it it in mind, thank you. We can remove the CR characters for coding style sometime down the line. darius.mihaim_gmail.com: Will keep it it in mind, thank you. We can remove the CR characters for coding style sometime… | |||||
fprintf(stderr, "%s: no such name: %s\r\n", | |||||
__func__, meta->dev_name); | |||||
memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); | |||||
return (0); | |||||
} | |||||
meta->dev_data = pdi; | |||||
if (pde->pe_snapshot == NULL) { | |||||
fprintf(stderr, "%s: not implemented yet for: %s\r\n", | |||||
__func__, meta->dev_name); | |||||
return (-1); | |||||
} | |||||
ret = pci_snapshot_pci_dev(meta); | |||||
if (ret != 0) { | |||||
fprintf(stderr, "%s: failed to snapshot pci dev\r\n", | |||||
__func__); | |||||
return (-1); | |||||
} | |||||
ret = (*pde->pe_snapshot)(meta); | |||||
return (ret); | |||||
} | |||||
int | |||||
pci_pause(struct vmctx *ctx, const char *dev_name) | |||||
{ | |||||
struct pci_devemu *pde; | |||||
struct pci_devinst *pdi; | |||||
int ret; | |||||
assert(dev_name != NULL); | |||||
ret = pci_find_slotted_dev(dev_name, &pde, &pdi); | |||||
if (ret != 0) { | |||||
/* | |||||
* It is possible to call this function without | |||||
* checking that the device is inserted first. | |||||
*/ | |||||
fprintf(stderr, "%s: no such name: %s\n", __func__, dev_name); | |||||
return (0); | |||||
} | |||||
if (pde->pe_pause == NULL) { | |||||
/* The pause/resume functionality is optional. */ | |||||
fprintf(stderr, "%s: not implemented for: %s\n", | |||||
__func__, dev_name); | |||||
return (0); | |||||
} | |||||
return (*pde->pe_pause)(ctx, pdi); | |||||
} | |||||
int | |||||
pci_resume(struct vmctx *ctx, const char *dev_name) | |||||
{ | |||||
struct pci_devemu *pde; | |||||
struct pci_devinst *pdi; | |||||
int ret; | |||||
assert(dev_name != NULL); | |||||
ret = pci_find_slotted_dev(dev_name, &pde, &pdi); | |||||
if (ret != 0) { | |||||
/* | |||||
* It is possible to call this function without | |||||
* checking that the device is inserted first. | |||||
*/ | |||||
fprintf(stderr, "%s: no such name: %s\n", __func__, dev_name); | |||||
return (0); | |||||
} | |||||
if (pde->pe_resume == NULL) { | |||||
/* The pause/resume functionality is optional. */ | |||||
fprintf(stderr, "%s: not implemented for: %s\n", | |||||
__func__, dev_name); | |||||
return (0); | |||||
} | |||||
return (*pde->pe_resume)(ctx, pdi); | |||||
} | |||||
#endif | |||||
#define PCI_EMUL_TEST | #define PCI_EMUL_TEST | ||||
#ifdef PCI_EMUL_TEST | #ifdef PCI_EMUL_TEST | ||||
/* | /* | ||||
* Define a dummy test device | * Define a dummy test device | ||||
*/ | */ | ||||
#define DIOSZ 8 | #define DIOSZ 8 | ||||
#define DMEMSZ 4096 | #define DMEMSZ 4096 | ||||
struct pci_emul_dsoftc { | struct pci_emul_dsoftc { | ||||
uint8_t ioregs[DIOSZ]; | uint8_t ioregs[DIOSZ]; | ||||
uint8_t memregs[2][DMEMSZ]; | uint8_t memregs[2][DMEMSZ]; | ||||
}; | }; | ||||
#define PCI_EMUL_MSI_MSGS 4 | #define PCI_EMUL_MSI_MSGS 4 | ||||
#define PCI_EMUL_MSIX_MSGS 16 | #define PCI_EMUL_MSIX_MSGS 16 | ||||
static int | static int | ||||
pci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, char *opts) | pci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, char *opts) | ||||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | if (size == 1) { | ||||
*(uint16_t *)&sc->memregs[i][offset] = value; | *(uint16_t *)&sc->memregs[i][offset] = value; | ||||
} else if (size == 4) { | } else if (size == 4) { | ||||
*(uint32_t *)&sc->memregs[i][offset] = value; | *(uint32_t *)&sc->memregs[i][offset] = value; | ||||
} else if (size == 8) { | } else if (size == 8) { | ||||
*(uint64_t *)&sc->memregs[i][offset] = value; | *(uint64_t *)&sc->memregs[i][offset] = value; | ||||
} else { | } else { | ||||
printf("diow: memw unknown size %d\n", size); | printf("diow: memw unknown size %d\n", size); | ||||
} | } | ||||
/* | /* | ||||
* magic interrupt ?? | * magic interrupt ?? | ||||
*/ | */ | ||||
} | } | ||||
if (baridx > 2 || baridx < 0) { | if (baridx > 2 || baridx < 0) { | ||||
printf("diow: unknown bar idx %d\n", baridx); | printf("diow: unknown bar idx %d\n", baridx); | ||||
} | } | ||||
} | } | ||||
static uint64_t | static uint64_t | ||||
pci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, | pci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, | ||||
uint64_t offset, int size) | uint64_t offset, int size) | ||||
{ | { | ||||
struct pci_emul_dsoftc *sc = pi->pi_arg; | struct pci_emul_dsoftc *sc = pi->pi_arg; | ||||
uint32_t value; | uint32_t value; | ||||
int i; | int i; | ||||
if (baridx == 0) { | if (baridx == 0) { | ||||
if (offset + size > DIOSZ) { | if (offset + size > DIOSZ) { | ||||
printf("dior: ior too large, offset %ld size %d\n", | printf("dior: ior too large, offset %ld size %d\n", | ||||
offset, size); | offset, size); | ||||
return (0); | return (0); | ||||
} | } | ||||
value = 0; | value = 0; | ||||
if (size == 1) { | if (size == 1) { | ||||
value = sc->ioregs[offset]; | value = sc->ioregs[offset]; | ||||
} else if (size == 2) { | } else if (size == 2) { | ||||
value = *(uint16_t *) &sc->ioregs[offset]; | value = *(uint16_t *) &sc->ioregs[offset]; | ||||
} else if (size == 4) { | } else if (size == 4) { | ||||
value = *(uint32_t *) &sc->ioregs[offset]; | value = *(uint32_t *) &sc->ioregs[offset]; | ||||
} else { | } else { | ||||
printf("dior: ior unknown size %d\n", size); | printf("dior: ior unknown size %d\n", size); | ||||
} | } | ||||
} | } | ||||
if (baridx == 1 || baridx == 2) { | if (baridx == 1 || baridx == 2) { | ||||
if (offset + size > DMEMSZ) { | if (offset + size > DMEMSZ) { | ||||
printf("dior: memr too large, offset %ld size %d\n", | printf("dior: memr too large, offset %ld size %d\n", | ||||
offset, size); | offset, size); | ||||
return (0); | return (0); | ||||
} | } | ||||
i = baridx - 1; /* 'memregs' index */ | i = baridx - 1; /* 'memregs' index */ | ||||
if (size == 1) { | if (size == 1) { | ||||
value = sc->memregs[i][offset]; | value = sc->memregs[i][offset]; | ||||
} else if (size == 2) { | } else if (size == 2) { | ||||
value = *(uint16_t *) &sc->memregs[i][offset]; | value = *(uint16_t *) &sc->memregs[i][offset]; | ||||
} else if (size == 4) { | } else if (size == 4) { | ||||
value = *(uint32_t *) &sc->memregs[i][offset]; | value = *(uint32_t *) &sc->memregs[i][offset]; | ||||
} else if (size == 8) { | } else if (size == 8) { | ||||
value = *(uint64_t *) &sc->memregs[i][offset]; | value = *(uint64_t *) &sc->memregs[i][offset]; | ||||
} else { | } else { | ||||
printf("dior: ior unknown size %d\n", size); | printf("dior: ior unknown size %d\n", size); | ||||
} | } | ||||
} | } | ||||
if (baridx > 2 || baridx < 0) { | if (baridx > 2 || baridx < 0) { | ||||
printf("dior: unknown bar idx %d\n", baridx); | printf("dior: unknown bar idx %d\n", baridx); | ||||
return (0); | return (0); | ||||
} | } | ||||
return (value); | return (value); | ||||
} | } | ||||
#ifdef BHYVE_SNAPSHOT | |||||
int | |||||
pci_emul_snapshot(struct vm_snapshot_meta *meta) | |||||
{ | |||||
return (0); | |||||
} | |||||
#endif | |||||
struct pci_devemu pci_dummy = { | struct pci_devemu pci_dummy = { | ||||
.pe_emu = "dummy", | .pe_emu = "dummy", | ||||
.pe_init = pci_emul_dinit, | .pe_init = pci_emul_dinit, | ||||
.pe_barwrite = pci_emul_diow, | .pe_barwrite = pci_emul_diow, | ||||
.pe_barread = pci_emul_dior | .pe_barread = pci_emul_dior, | ||||
#ifdef BHYVE_SNAPSHOT | |||||
.pe_snapshot = pci_emul_snapshot, | |||||
#endif | |||||
}; | }; | ||||
PCI_EMUL_SET(pci_dummy); | PCI_EMUL_SET(pci_dummy); | ||||
#endif /* PCI_EMUL_TEST */ | #endif /* PCI_EMUL_TEST */ |
I get these messages for devices that I don't actually have in a VM, is that expected behavior? Also, why is that using dos-style (\r\n) line endings?