Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_emul.c
Show First 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | |||||
#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) | ||||
struct funcinfo { | struct funcinfo { | ||||
nvlist_t *fi_config; | config_node_t *fi_config; | ||||
struct pci_devemu *fi_pde; | struct pci_devemu *fi_pde; | ||||
struct pci_devinst *fi_devi; | struct pci_devinst *fi_devi; | ||||
}; | }; | ||||
struct intxinfo { | struct intxinfo { | ||||
int ii_count; | int ii_count; | ||||
int ii_pirq_pin; | int ii_pirq_pin; | ||||
int ii_ioapic_irq; | int ii_ioapic_irq; | ||||
▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Helper function to parse a list of comma-separated options where | * Helper function to parse a list of comma-separated options where | ||||
* each option is formatted as "name[=value]". If no value is | * each option is formatted as "name[=value]". If no value is | ||||
* provided, the option is treated as a boolean and is given a value | * provided, the option is treated as a boolean and is given a value | ||||
* of true. | * of true. | ||||
*/ | */ | ||||
int | int | ||||
pci_parse_legacy_config(nvlist_t *nvl, const char *opt) | pci_parse_legacy_config(config_node_t *node, const char *opt) | ||||
{ | { | ||||
char *config, *name, *tofree, *value; | char *config, *name, *tofree, *value; | ||||
if (opt == NULL) | if (opt == NULL) | ||||
return (0); | return (0); | ||||
config = tofree = strdup(opt); | config = tofree = strdup(opt); | ||||
while ((name = strsep(&config, ",")) != NULL) { | while ((name = strsep(&config, ",")) != NULL) { | ||||
value = strchr(name, '='); | value = strchr(name, '='); | ||||
if (value != NULL) { | if (value != NULL) { | ||||
*value = '\0'; | *value = '\0'; | ||||
value++; | value++; | ||||
set_config_value_node(nvl, name, value); | set_config_value_node(node, name, value); | ||||
} else | } else | ||||
set_config_bool_node(nvl, name, true); | set_config_bool_node(node, name, true); | ||||
} | } | ||||
free(tofree); | free(tofree); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* PCI device configuration is stored in MIBs that encode the device's | * PCI device configuration is stored in MIBs that encode the device's | ||||
* location: | * location: | ||||
Show All 10 Lines | |||||
*/ | */ | ||||
int | int | ||||
pci_parse_slot(char *opt) | pci_parse_slot(char *opt) | ||||
{ | { | ||||
char node_name[sizeof("pci.XXX.XX.X")]; | char node_name[sizeof("pci.XXX.XX.X")]; | ||||
struct pci_devemu *pde; | struct pci_devemu *pde; | ||||
char *emul, *config, *str, *cp; | char *emul, *config, *str, *cp; | ||||
int error, bnum, snum, fnum; | int error, bnum, snum, fnum; | ||||
nvlist_t *nvl; | config_node_t *node; | ||||
error = -1; | error = -1; | ||||
str = strdup(opt); | str = strdup(opt); | ||||
emul = config = NULL; | emul = config = NULL; | ||||
if ((cp = strchr(str, ',')) != NULL) { | if ((cp = strchr(str, ',')) != NULL) { | ||||
*cp = '\0'; | *cp = '\0'; | ||||
emul = cp + 1; | emul = cp + 1; | ||||
Show All 29 Lines | pci_parse_slot(char *opt) | ||||
if (pde == NULL) { | if (pde == NULL) { | ||||
EPRINTLN("pci slot %d:%d:%d: unknown device \"%s\"", bnum, snum, | EPRINTLN("pci slot %d:%d:%d: unknown device \"%s\"", bnum, snum, | ||||
fnum, emul); | fnum, emul); | ||||
goto done; | goto done; | ||||
} | } | ||||
snprintf(node_name, sizeof(node_name), "pci.%d.%d.%d", bnum, snum, | snprintf(node_name, sizeof(node_name), "pci.%d.%d.%d", bnum, snum, | ||||
fnum); | fnum); | ||||
nvl = find_config_node(node_name); | node = find_config_node(node_name); | ||||
if (nvl != NULL) { | if (node != NULL) { | ||||
EPRINTLN("pci slot %d:%d:%d already occupied!", bnum, snum, | EPRINTLN("pci slot %d:%d:%d already occupied!", bnum, snum, | ||||
fnum); | fnum); | ||||
goto done; | goto done; | ||||
} | } | ||||
nvl = create_config_node(node_name); | node = create_config_node(node_name); | ||||
if (pde->pe_alias != NULL) | if (pde->pe_alias != NULL) | ||||
set_config_value_node(nvl, "device", pde->pe_alias); | set_config_value_node(node, "device", pde->pe_alias); | ||||
else | else | ||||
set_config_value_node(nvl, "device", pde->pe_emu); | set_config_value_node(node, "device", pde->pe_emu); | ||||
if (pde->pe_legacy_config != NULL) | if (pde->pe_legacy_config != NULL) | ||||
error = pde->pe_legacy_config(nvl, config); | error = pde->pe_legacy_config(node, config); | ||||
else | else | ||||
error = pci_parse_legacy_config(nvl, config); | error = pci_parse_legacy_config(node, config); | ||||
done: | done: | ||||
free(str); | free(str); | ||||
return (error); | return (error); | ||||
} | } | ||||
void | void | ||||
pci_print_supported_devices() | pci_print_supported_devices() | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 845 Lines • ▼ Show 20 Lines | |||||
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; | ||||
struct funcinfo *fi; | struct funcinfo *fi; | ||||
nvlist_t *nvl; | config_node_t *node; | ||||
const char *emul; | const char *emul; | ||||
size_t lowmem; | size_t lowmem; | ||||
uint64_t cpu_maxphysaddr, pci_emul_memresv64; | uint64_t cpu_maxphysaddr, pci_emul_memresv64; | ||||
u_int regs[4]; | u_int regs[4]; | ||||
int bus, slot, func, error; | int bus, slot, func, error; | ||||
pci_emul_iobase = PCI_EMUL_IOBASE; | pci_emul_iobase = PCI_EMUL_IOBASE; | ||||
pci_emul_membase32 = vm_get_lowmem_limit(ctx); | pci_emul_membase32 = vm_get_lowmem_limit(ctx); | ||||
do_cpuid(0x80000008, regs); | do_cpuid(0x80000008, regs); | ||||
cpu_maxphysaddr = 1ULL << (regs[0] & 0xff); | cpu_maxphysaddr = 1ULL << (regs[0] & 0xff); | ||||
if (cpu_maxphysaddr > VM_MAXUSER_ADDRESS_LA48) | if (cpu_maxphysaddr > VM_MAXUSER_ADDRESS_LA48) | ||||
cpu_maxphysaddr = VM_MAXUSER_ADDRESS_LA48; | cpu_maxphysaddr = VM_MAXUSER_ADDRESS_LA48; | ||||
pci_emul_memresv64 = cpu_maxphysaddr / 4; | pci_emul_memresv64 = cpu_maxphysaddr / 4; | ||||
/* | /* | ||||
* Max power of 2 that is less then | * Max power of 2 that is less then | ||||
* cpu_maxphysaddr - pci_emul_memresv64. | * cpu_maxphysaddr - pci_emul_memresv64. | ||||
*/ | */ | ||||
pci_emul_membase64 = 1ULL << (flsl(cpu_maxphysaddr - | pci_emul_membase64 = 1ULL << (flsl(cpu_maxphysaddr - | ||||
pci_emul_memresv64) - 1); | pci_emul_memresv64) - 1); | ||||
pci_emul_memlim64 = cpu_maxphysaddr; | pci_emul_memlim64 = cpu_maxphysaddr; | ||||
for (bus = 0; bus < MAXBUSES; bus++) { | for (bus = 0; bus < MAXBUSES; bus++) { | ||||
snprintf(node_name, sizeof(node_name), "pci.%d", bus); | snprintf(node_name, sizeof(node_name), "pci.%d", bus); | ||||
nvl = find_config_node(node_name); | node = find_config_node(node_name); | ||||
if (nvl == NULL) | if (node == NULL) | ||||
continue; | continue; | ||||
pci_businfo[bus] = calloc(1, sizeof(struct businfo)); | pci_businfo[bus] = calloc(1, sizeof(struct businfo)); | ||||
bi = pci_businfo[bus]; | bi = pci_businfo[bus]; | ||||
/* | /* | ||||
* Keep track of the i/o and memory resources allocated to | * Keep track of the i/o and memory resources allocated to | ||||
* this bus. | * this bus. | ||||
*/ | */ | ||||
bi->iobase = pci_emul_iobase; | bi->iobase = pci_emul_iobase; | ||||
bi->membase32 = pci_emul_membase32; | bi->membase32 = pci_emul_membase32; | ||||
bi->membase64 = pci_emul_membase64; | bi->membase64 = pci_emul_membase64; | ||||
for (slot = 0; slot < MAXSLOTS; slot++) { | for (slot = 0; slot < MAXSLOTS; slot++) { | ||||
si = &bi->slotinfo[slot]; | si = &bi->slotinfo[slot]; | ||||
for (func = 0; func < MAXFUNCS; func++) { | for (func = 0; func < MAXFUNCS; func++) { | ||||
fi = &si->si_funcs[func]; | fi = &si->si_funcs[func]; | ||||
snprintf(node_name, sizeof(node_name), | snprintf(node_name, sizeof(node_name), | ||||
"pci.%d.%d.%d", bus, slot, func); | "pci.%d.%d.%d", bus, slot, func); | ||||
nvl = find_config_node(node_name); | node = find_config_node(node_name); | ||||
if (nvl == NULL) | if (node == NULL) | ||||
continue; | continue; | ||||
fi->fi_config = nvl; | fi->fi_config = node; | ||||
emul = get_config_value_node(nvl, "device"); | emul = get_config_value_node(node, "device"); | ||||
if (emul == NULL) { | if (emul == NULL) { | ||||
EPRINTLN("pci slot %d:%d:%d: missing " | EPRINTLN("pci slot %d:%d:%d: missing " | ||||
"\"device\" value", bus, slot, func); | "\"device\" value", bus, slot, func); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
pde = pci_emul_finddev(emul); | pde = pci_emul_finddev(emul); | ||||
if (pde == NULL) { | if (pde == NULL) { | ||||
EPRINTLN("pci slot %d:%d:%d: unknown " | EPRINTLN("pci slot %d:%d:%d: unknown " | ||||
▲ Show 20 Lines • Show All 1,042 Lines • ▼ Show 20 Lines | 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, nvlist_t *nvl) | pci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, config_node_t *node) | ||||
{ | { | ||||
int error; | int error; | ||||
struct pci_emul_dsoftc *sc; | struct pci_emul_dsoftc *sc; | ||||
sc = calloc(1, sizeof(struct pci_emul_dsoftc)); | sc = calloc(1, sizeof(struct pci_emul_dsoftc)); | ||||
pi->pi_arg = sc; | pi->pi_arg = sc; | ||||
▲ Show 20 Lines • Show All 165 Lines • Show Last 20 Lines |