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 "inout.h" | #include "inout.h" | ||||
#include "ioapic.h" | #include "ioapic.h" | ||||
#include "mem.h" | #include "mem.h" | ||||
#include "pci_emul.h" | #include "pci_emul.h" | ||||
▲ Show 20 Lines • Show All 1,875 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); | ||||
/* | |||||
* 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) { | |||||
fprintf(stderr, "%s: no such name: %s\r\n", | |||||
novel: I get these messages for devices that I don't actually have in a VM, is that expected behavior? | |||||
darius.mihaim_gmail.comUnsubmitted 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… | |||||
jhbAuthorUnsubmitted 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… | |||||
darius.mihaim_gmail.comUnsubmitted 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… | |||||
__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); | |||||
} | |||||
#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 { | ||||
▲ Show 20 Lines • Show All 153 Lines • ▼ Show 20 Lines | pci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, | ||||
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); | ||||
} | } | ||||
int | |||||
pci_emul_snapshot(struct vm_snapshot_meta *meta) | |||||
{ | |||||
return (0); | |||||
} | |||||
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, | ||||
.pe_snapshot = pci_emul_snapshot, | |||||
}; | }; | ||||
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?