Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_emul.c
Show First 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | |||||
#include "debug.h" | #include "debug.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" | ||||
#include "pci_irq.h" | #include "pci_irq.h" | ||||
#include "pci_lpc.h" | #include "pci_lpc.h" | ||||
#ifdef BHYVE_SNAPSHOT | |||||
#include "snapshot.h" | |||||
#endif | |||||
#define CONF1_ADDR_PORT 0x0cf8 | #define CONF1_ADDR_PORT 0x0cf8 | ||||
#define CONF1_DATA_PORT 0x0cfc | #define CONF1_DATA_PORT 0x0cfc | ||||
#define CONF1_ENABLE 0x80000000ul | #define CONF1_ENABLE 0x80000000ul | ||||
#define MAXBUSES (PCI_BUSMAX + 1) | #define MAXBUSES (PCI_BUSMAX + 1) | ||||
#define MAXSLOTS (PCI_SLOTMAX + 1) | #define MAXSLOTS (PCI_SLOTMAX + 1) | ||||
#define MAXFUNCS (PCI_FUNCMAX + 1) | #define MAXFUNCS (PCI_FUNCMAX + 1) | ||||
▲ Show 20 Lines • Show All 903 Lines • ▼ Show 20 Lines | pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot, | ||||
pdi->pi_slot = slot; | pdi->pi_slot = slot; | ||||
pdi->pi_func = func; | pdi->pi_func = func; | ||||
pthread_mutex_init(&pdi->pi_lintr.lock, NULL); | pthread_mutex_init(&pdi->pi_lintr.lock, NULL); | ||||
pdi->pi_lintr.pin = 0; | pdi->pi_lintr.pin = 0; | ||||
pdi->pi_lintr.state = IDLE; | pdi->pi_lintr.state = IDLE; | ||||
pdi->pi_lintr.pirq_pin = 0; | pdi->pi_lintr.pirq_pin = 0; | ||||
pdi->pi_lintr.ioapic_irq = 0; | pdi->pi_lintr.ioapic_irq = 0; | ||||
pdi->pi_d = pde; | pdi->pi_d = pde; | ||||
snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot); | |||||
snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d-%d-%d", pde->pe_emu, bus, | |||||
corvink: IMHO, we should split this change into a separate commit. | |||||
Done Inline ActionsWill do. Thanks! gusev.vitaliy_gmail.com: Will do. Thanks! | |||||
slot, func); | |||||
/* Disable legacy interrupts */ | /* Disable legacy interrupts */ | ||||
pci_set_cfgdata8(pdi, PCIR_INTLINE, 255); | pci_set_cfgdata8(pdi, PCIR_INTLINE, 255); | ||||
pci_set_cfgdata8(pdi, PCIR_INTPIN, 0); | pci_set_cfgdata8(pdi, PCIR_INTPIN, 0); | ||||
pci_set_cfgdata8(pdi, PCIR_COMMAND, PCIM_CMD_BUSMASTEREN); | pci_set_cfgdata8(pdi, PCIR_COMMAND, PCIM_CMD_BUSMASTEREN); | ||||
err = (*pde->pe_init)(ctx, pdi, fi->fi_config); | err = (*pde->pe_init)(ctx, pdi, fi->fi_config); | ||||
if (err == 0) | if (err == 0) | ||||
▲ Show 20 Lines • Show All 317 Lines • ▼ Show 20 Lines | pci_ecfg_base(void) | ||||
return (PCI_EMUL_ECFG_BASE); | return (PCI_EMUL_ECFG_BASE); | ||||
} | } | ||||
#define BUSIO_ROUNDUP 32 | #define BUSIO_ROUNDUP 32 | ||||
#define BUSMEM32_ROUNDUP (1024 * 1024) | #define BUSMEM32_ROUNDUP (1024 * 1024) | ||||
#define BUSMEM64_ROUNDUP (512 * 1024 * 1024) | #define BUSMEM64_ROUNDUP (512 * 1024 * 1024) | ||||
#ifdef BHYVE_SNAPSHOT | |||||
static int pci_snapshot(struct vm_snapshot_meta *, void *); | |||||
static int pci_pause(struct vmctx *, void *); | |||||
static int pci_resume(struct vmctx *, void *); | |||||
static struct snapshot_ops pci_snapshot_ops = { | |||||
.snapshot_cb = pci_snapshot, | |||||
.pause_cb = pci_pause, | |||||
.resume_cb = pci_resume, | |||||
}; | |||||
#endif | |||||
int | int | ||||
init_pci(struct vmctx *ctx) | init_pci(struct vmctx *ctx) | ||||
{ | { | ||||
char node_name[sizeof("pci.XXX.XX.X")]; | char node_name[sizeof("pci.XXX.XX.X")]; | ||||
struct mem_range mr; | struct mem_range mr; | ||||
struct pci_devemu *pde; | struct pci_devemu *pde; | ||||
struct businfo *bi; | struct businfo *bi; | ||||
struct slotinfo *si; | struct slotinfo *si; | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | for (slot = 0; slot < MAXSLOTS; slot++) { | ||||
pde->pe_alias); | pde->pe_alias); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
fi->fi_pde = pde; | fi->fi_pde = pde; | ||||
error = pci_emul_init(ctx, pde, bus, slot, | error = pci_emul_init(ctx, pde, bus, slot, | ||||
func, fi); | func, fi); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
#ifdef BHYVE_SNAPSHOT | |||||
register_snapshot_dev(fi->fi_devi->pi_name, | |||||
&pci_snapshot_ops, fi->fi_devi); | |||||
#endif | |||||
} | } | ||||
} | } | ||||
/* second run: assign BARs and free list */ | /* second run: assign BARs and free list */ | ||||
struct pci_bar_allocation *bar; | struct pci_bar_allocation *bar; | ||||
struct pci_bar_allocation *bar_tmp; | struct pci_bar_allocation *bar_tmp; | ||||
TAILQ_FOREACH_SAFE(bar, &pci_bars, chain, bar_tmp) { | TAILQ_FOREACH_SAFE(bar, &pci_bars, chain, bar_tmp) { | ||||
pci_emul_assign_bar(bar->pdi, bar->idx, bar->type, | pci_emul_assign_bar(bar->pdi, bar->idx, bar->type, | ||||
▲ Show 20 Lines • Show All 907 Lines • ▼ Show 20 Lines | SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].vector_control, | ||||
meta, ret, done); | meta, ret, done); | ||||
} | } | ||||
done: | done: | ||||
return (ret); | return (ret); | ||||
} | } | ||||
static int | static int | ||||
pci_find_slotted_dev(const char *dev_name, struct pci_devemu **pde, | pci_snapshot(struct vm_snapshot_meta *meta, void *cbdata) | ||||
Not Done Inline ActionsWhat's the advantage of a void pointer instead of a struct pci_devinst pointer? I'd prefer struct pci_devinst. corvink: What's the advantage of a `void` pointer instead of a `struct pci_devinst` pointer? I'd prefer… | |||||
Done Inline ActionsIt is because static struct snapshot_ops (snapshot_cb) is general for all type of devices. Not only PCI. For instance, look at gusev.vitaliy_gmail.com: It is because static struct snapshot_ops (snapshot_cb) is general for all type of devices. Not… | |||||
Not Done Inline ActionsGot it. corvink: Got it. | |||||
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_pde == NULL) | |||||
continue; | |||||
if (strcmp(dev_name, fi->fi_pde->pe_emu) != 0) | |||||
continue; | |||||
*pde = fi->fi_pde; | |||||
*pdi = fi->fi_devi; | |||||
return (0); | |||||
} | |||||
} | |||||
} | |||||
return (EINVAL); | |||||
} | |||||
int | |||||
pci_snapshot(struct vm_snapshot_meta *meta) | |||||
{ | |||||
struct pci_devemu *pde; | struct pci_devemu *pde; | ||||
struct pci_devinst *pdi; | struct pci_devinst *pdi = (struct pci_devinst *)cbdata; | ||||
int ret; | int ret; | ||||
assert(meta->dev_name != NULL); | assert(pdi != NULL); | ||||
ret = pci_find_slotted_dev(meta->dev_name, &pde, &pdi); | pde = pdi->pi_d; | ||||
if (ret != 0) { | |||||
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; | meta->dev_data = pdi; | ||||
if (pde->pe_snapshot == NULL) { | if (pde->pe_snapshot == NULL) { | ||||
fprintf(stderr, "%s: not implemented yet for: %s\r\n", | fprintf(stderr, "%s: not implemented yet for: %s\r\n", | ||||
__func__, meta->dev_name); | __func__, meta->dev_name); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
ret = pci_snapshot_pci_dev(meta); | ret = pci_snapshot_pci_dev(meta); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
fprintf(stderr, "%s: failed to snapshot pci dev\r\n", | fprintf(stderr, "%s: failed to snapshot pci dev %s: %d\n", | ||||
__func__); | __func__, meta->dev_name, ret); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
ret = (*pde->pe_snapshot)(meta); | ret = (*pde->pe_snapshot)(meta); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
int | static int | ||||
pci_pause(struct vmctx *ctx, const char *dev_name) | pci_pause(struct vmctx *ctx, void *cbdata) | ||||
{ | { | ||||
struct pci_devemu *pde; | struct pci_devemu *pde; | ||||
struct pci_devinst *pdi; | struct pci_devinst *pdi = (struct pci_devinst *)cbdata; | ||||
int ret; | |||||
assert(dev_name != NULL); | assert(pdi != NULL); | ||||
ret = pci_find_slotted_dev(dev_name, &pde, &pdi); | pde = pdi->pi_d; | ||||
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) { | if (pde->pe_pause == NULL) { | ||||
/* The pause/resume functionality is optional. */ | /* The pause/resume functionality is optional. */ | ||||
fprintf(stderr, "%s: not implemented for: %s\n", | fprintf(stderr, "%s: not implemented for: %s\n", | ||||
__func__, dev_name); | __func__, pdi->pi_name); | ||||
return (0); | return (0); | ||||
} | } | ||||
return (*pde->pe_pause)(ctx, pdi); | return (*pde->pe_pause)(ctx, pdi); | ||||
} | } | ||||
int | static int | ||||
pci_resume(struct vmctx *ctx, const char *dev_name) | pci_resume(struct vmctx *ctx, void *cbdata) | ||||
{ | { | ||||
struct pci_devemu *pde; | struct pci_devemu *pde; | ||||
struct pci_devinst *pdi; | struct pci_devinst *pdi = (struct pci_devinst *)cbdata; | ||||
int ret; | |||||
assert(dev_name != NULL); | assert(pdi != NULL); | ||||
ret = pci_find_slotted_dev(dev_name, &pde, &pdi); | pde = pdi->pi_d; | ||||
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) { | if (pde->pe_resume == NULL) { | ||||
/* The pause/resume functionality is optional. */ | /* The pause/resume functionality is optional. */ | ||||
fprintf(stderr, "%s: not implemented for: %s\n", | fprintf(stderr, "%s: not implemented for: %s\n", | ||||
__func__, dev_name); | __func__, pdi->pi_name); | ||||
return (0); | return (0); | ||||
} | } | ||||
return (*pde->pe_resume)(ctx, pdi); | return (*pde->pe_resume)(ctx, pdi); | ||||
} | } | ||||
#endif | #endif | ||||
#define PCI_EMUL_TEST | #define PCI_EMUL_TEST | ||||
▲ Show 20 Lines • Show All 161 Lines • ▼ Show 20 Lines | 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 | #ifdef BHYVE_SNAPSHOT | ||||
int | static int | ||||
pci_emul_snapshot(struct vm_snapshot_meta *meta) | pci_emul_snapshot(struct vm_snapshot_meta *meta) | ||||
{ | { | ||||
return (0); | return (0); | ||||
} | } | ||||
#endif | #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 | #ifdef BHYVE_SNAPSHOT | ||||
.pe_snapshot = pci_emul_snapshot, | .pe_snapshot = pci_emul_snapshot, | ||||
#endif | #endif | ||||
}; | }; | ||||
PCI_EMUL_SET(pci_dummy); | PCI_EMUL_SET(pci_dummy); | ||||
#endif /* PCI_EMUL_TEST */ | #endif /* PCI_EMUL_TEST */ |
IMHO, we should split this change into a separate commit.