diff options
Diffstat (limited to 'usr/src/cmd/bhyve/pci_hostbridge.c')
| -rw-r--r-- | usr/src/cmd/bhyve/pci_hostbridge.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/usr/src/cmd/bhyve/pci_hostbridge.c b/usr/src/cmd/bhyve/pci_hostbridge.c new file mode 100644 index 0000000000..b926c7817e --- /dev/null +++ b/usr/src/cmd/bhyve/pci_hostbridge.c @@ -0,0 +1,236 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2011 NetApp, Inc. + * Copyright (c) 2018 Joyent, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/cdefs.h> +#ifndef __FreeBSD__ +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <strings.h> +#endif +__FBSDID("$FreeBSD$"); + +#include "pci_emul.h" + +#ifdef __FreeBSD__ +static int +pci_hostbridge_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) +{ + + /* config space */ + pci_set_cfgdata16(pi, PCIR_VENDOR, 0x1275); /* NetApp */ + pci_set_cfgdata16(pi, PCIR_DEVICE, 0x1275); /* NetApp */ + pci_set_cfgdata8(pi, PCIR_HDRTYPE, PCIM_HDRTYPE_NORMAL); + pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_BRIDGE); + pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_BRIDGE_HOST); + + pci_emul_add_pciecap(pi, PCIEM_TYPE_ROOT_PORT); + + return (0); +} + +static int +pci_amd_hostbridge_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) +{ + (void) pci_hostbridge_init(ctx, pi, opts); + pci_set_cfgdata16(pi, PCIR_VENDOR, 0x1022); /* AMD */ + pci_set_cfgdata16(pi, PCIR_DEVICE, 0x7432); /* made up */ + + return (0); +} +#else +static void +pci_hostbridge_setup(struct pci_devinst *pi, uint16_t vendor, uint16_t device) +{ + /* config space */ + pci_set_cfgdata16(pi, PCIR_VENDOR, vendor); + pci_set_cfgdata16(pi, PCIR_DEVICE, device); + pci_set_cfgdata8(pi, PCIR_HDRTYPE, PCIM_HDRTYPE_NORMAL); + pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_BRIDGE); + pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_BRIDGE_HOST); + + pci_emul_add_pciecap(pi, PCIEM_TYPE_ROOT_PORT); +} + + +static int +pci_hostbridge_parse_pci_val(const char *in, uint16_t *val) +{ + long num; + char *endp = NULL; + + errno = 0; + num = strtol(in, &endp, 0); + if (errno != 0 || endp == NULL || *endp != '\0') { + fprintf(stderr, "pci_hostbridge: invalid num '%s'", in); + return (-1); + } else if (num < 1 || num > UINT16_MAX) { + fprintf(stderr, "pci_hostbridge: 0x%04lx out of range", num); + return (-1); + } + *val = num; + return (0); +} + +static struct pci_hostbridge_model { + const char *phm_model; + uint16_t phm_vendor; + uint16_t phm_device; +} pci_hb_models[] = { + { "amd", 0x1022, 0x7432 }, /* AMD/made-up */ + { "netapp", 0x1275, 0x1275 }, /* NetApp/NetApp */ + { "i440fx", 0x8086, 0x1237 }, /* Intel/82441 */ + { "q35", 0x8086, 0x29b0 }, /* Intel/Q35 HB */ +}; + +#define NUM_HB_MODELS (sizeof (pci_hb_models) / sizeof (pci_hb_models[0])) + +static int +pci_hostbridge_parse_args(char *opts, uint16_t *vendorp, uint16_t *devicep) +{ + const char *model = NULL; + char *next; + uint16_t vendor = 0, device = 0; + int err = 0; + + for (; opts != NULL && *opts != '\0'; opts = next) { + char *val, *cp; + + if ((cp = strchr(opts, ',')) != NULL) { + *cp = '\0'; + next = cp + 1; + } else { + next = NULL; + } + + if ((cp = strchr(opts, '=')) == NULL) { + fprintf(stderr, + "pci_hostbridge: expected value for param" + " (%s=VAL)", opts); + err = -1; + continue; + } + + /* <param>=<value> handling */ + val = cp + 1; + *cp = '\0'; + if (strcmp(opts, "model") == 0) { + model = val; + } else if (strcmp(opts, "vendor") == 0) { + if (pci_hostbridge_parse_pci_val(val, &vendor) != 0) { + err = -1; + continue; + } + } else if (strcmp(opts, "device") == 0) { + if (pci_hostbridge_parse_pci_val(val, &device) != 0) { + err = -1; + continue; + } + } else { + fprintf(stderr, + "pci_hostbridge: unrecognized option '%s'", opts); + err = -1; + continue; + } + } + if (err != 0) { + return (err); + } + + if (model != NULL && (vendor != 0 || device != 0)) { + fprintf(stderr, "pci_hostbridge: cannot specify model " + "and vendor/device"); + return (-1); + } else if ((vendor != 0 && device == 0) || + (vendor == 0 && device != 0)) { + fprintf(stderr, "pci_hostbridge: must specify both vendor and" + "device for custom hostbridge"); + return (-1); + } + if (model != NULL) { + uint_t i; + + for (i = 0; i < NUM_HB_MODELS; i++) { + if (strcmp(model, pci_hb_models[i].phm_model) != 0) + continue; + + /* found a model match */ + *vendorp = pci_hb_models[i].phm_vendor; + *devicep = pci_hb_models[i].phm_device; + return (0); + } + fprintf(stderr, "pci_hostbridge: invalid model '%s'", model); + return (-1); + } + + /* custom hostbridge ID was specified */ + *vendorp = vendor; + *devicep = device; + return (0); +} + +static int +pci_hostbridge_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) +{ + uint16_t vendor, device; + + if (opts == NULL) { + /* Fall back to NetApp default if no options are specified */ + vendor = 0x1275; + device = 0x1275; + } else if (pci_hostbridge_parse_args(opts, &vendor, &device) != 0) { + return (-1); + } + + pci_hostbridge_setup(pi, vendor, device); + return (0); +} + +static int +pci_amd_hostbridge_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) +{ + pci_hostbridge_setup(pi, 0x1022, 0x7432); + return (0); +} + +#endif /* __FreeBSD__ */ + +struct pci_devemu pci_de_amd_hostbridge = { + .pe_emu = "amd_hostbridge", + .pe_init = pci_amd_hostbridge_init, +}; +PCI_EMUL_SET(pci_de_amd_hostbridge); + +struct pci_devemu pci_de_hostbridge = { + .pe_emu = "hostbridge", + .pe_init = pci_hostbridge_init, +}; +PCI_EMUL_SET(pci_de_hostbridge); |
