xref: /illumos-gate/usr/src/cmd/bhyve/pci_hostbridge.c (revision 32640292339b07090f10ce34d455f98711077343)
1bf21cd93STycho Nightingale /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
34c87aefeSPatrick Mooney  *
4bf21cd93STycho Nightingale  * Copyright (c) 2011 NetApp, Inc.
54c87aefeSPatrick Mooney  * Copyright (c) 2018 Joyent, Inc.
69e6c6f2fSPatrick Mooney  * Copyright 2021 Oxide Computer Company
7bf21cd93STycho Nightingale  * All rights reserved.
8bf21cd93STycho Nightingale  *
9bf21cd93STycho Nightingale  * Redistribution and use in source and binary forms, with or without
10bf21cd93STycho Nightingale  * modification, are permitted provided that the following conditions
11bf21cd93STycho Nightingale  * are met:
12bf21cd93STycho Nightingale  * 1. Redistributions of source code must retain the above copyright
13bf21cd93STycho Nightingale  *    notice, this list of conditions and the following disclaimer.
14bf21cd93STycho Nightingale  * 2. Redistributions in binary form must reproduce the above copyright
15bf21cd93STycho Nightingale  *    notice, this list of conditions and the following disclaimer in the
16bf21cd93STycho Nightingale  *    documentation and/or other materials provided with the distribution.
17bf21cd93STycho Nightingale  *
18bf21cd93STycho Nightingale  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
19bf21cd93STycho Nightingale  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20bf21cd93STycho Nightingale  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21bf21cd93STycho Nightingale  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
22bf21cd93STycho Nightingale  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23bf21cd93STycho Nightingale  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24bf21cd93STycho Nightingale  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25bf21cd93STycho Nightingale  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26bf21cd93STycho Nightingale  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27bf21cd93STycho Nightingale  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28bf21cd93STycho Nightingale  * SUCH DAMAGE.
29bf21cd93STycho Nightingale  */
30bf21cd93STycho Nightingale 
31bf21cd93STycho Nightingale #include <sys/cdefs.h>
324c87aefeSPatrick Mooney #ifndef __FreeBSD__
334c87aefeSPatrick Mooney #include <errno.h>
344c87aefeSPatrick Mooney #include <stdlib.h>
354c87aefeSPatrick Mooney #include <stdio.h>
364c87aefeSPatrick Mooney #include <strings.h>
37*32640292SAndy Fiddaman #include <err.h>
384c87aefeSPatrick Mooney #endif
39bf21cd93STycho Nightingale 
402b948146SAndy Fiddaman #include <stdlib.h>
412b948146SAndy Fiddaman 
422b948146SAndy Fiddaman #include "config.h"
43bf21cd93STycho Nightingale #include "pci_emul.h"
449e6c6f2fSPatrick Mooney #ifndef __FreeBSD__
459e6c6f2fSPatrick Mooney #include "bhyverun.h"
469e6c6f2fSPatrick Mooney #endif
47bf21cd93STycho Nightingale 
482b948146SAndy Fiddaman #ifndef __FreeBSD__
494c87aefeSPatrick Mooney static struct pci_hostbridge_model {
504c87aefeSPatrick Mooney 	const char	*phm_model;
514c87aefeSPatrick Mooney 	uint16_t	phm_vendor;
524c87aefeSPatrick Mooney 	uint16_t	phm_device;
534c87aefeSPatrick Mooney } pci_hb_models[] = {
544c87aefeSPatrick Mooney 	{ "amd",	0x1022, 0x7432 }, /* AMD/made-up */
554c87aefeSPatrick Mooney 	{ "netapp",	0x1275, 0x1275 }, /* NetApp/NetApp */
564c87aefeSPatrick Mooney 	{ "i440fx",	0x8086, 0x1237 }, /* Intel/82441 */
574c87aefeSPatrick Mooney 	{ "q35",	0x8086, 0x29b0 }, /* Intel/Q35 HB */
584c87aefeSPatrick Mooney };
594c87aefeSPatrick Mooney 
604c87aefeSPatrick Mooney #define	NUM_HB_MODELS	(sizeof (pci_hb_models) / sizeof (pci_hb_models[0]))
612b948146SAndy Fiddaman #endif
624c87aefeSPatrick Mooney 
634c87aefeSPatrick Mooney static int
pci_hostbridge_init(struct pci_devinst * pi,nvlist_t * nvl)64*32640292SAndy Fiddaman pci_hostbridge_init(struct pci_devinst *pi, nvlist_t *nvl)
654c87aefeSPatrick Mooney {
662b948146SAndy Fiddaman 	const char *value;
672b948146SAndy Fiddaman 	u_int vendor, device;
684c87aefeSPatrick Mooney 
692b948146SAndy Fiddaman #ifdef __FreeBSD__
702b948146SAndy Fiddaman 	vendor = 0x1275;	/* NetApp */
712b948146SAndy Fiddaman 	device = 0x1275;	/* NetApp */
722b948146SAndy Fiddaman #else
732b948146SAndy Fiddaman 	vendor = device = 0;
742b948146SAndy Fiddaman #endif
754c87aefeSPatrick Mooney 
762b948146SAndy Fiddaman 	value = get_config_value_node(nvl, "vendor");
772b948146SAndy Fiddaman 	if (value != NULL)
782b948146SAndy Fiddaman 		vendor = strtol(value, NULL, 0);
79*32640292SAndy Fiddaman 	else
80*32640292SAndy Fiddaman 		vendor = pci_config_read_reg(NULL, nvl, PCIR_VENDOR, 2, vendor);
812b948146SAndy Fiddaman 	value = get_config_value_node(nvl, "devid");
822b948146SAndy Fiddaman 	if (value != NULL)
832b948146SAndy Fiddaman 		device = strtol(value, NULL, 0);
84*32640292SAndy Fiddaman 	else
85*32640292SAndy Fiddaman 		device = pci_config_read_reg(NULL, nvl, PCIR_DEVICE, 2, device);
864c87aefeSPatrick Mooney 
872b948146SAndy Fiddaman #ifndef __FreeBSD__
882b948146SAndy Fiddaman 	const char *model = get_config_value_node(nvl, "model");
894c87aefeSPatrick Mooney 
904c87aefeSPatrick Mooney 	if (model != NULL && (vendor != 0 || device != 0)) {
91*32640292SAndy Fiddaman 		warnx("pci_hostbridge: cannot specify model and vendor/device");
924c87aefeSPatrick Mooney 		return (-1);
934c87aefeSPatrick Mooney 	} else if ((vendor != 0 && device == 0) ||
944c87aefeSPatrick Mooney 	    (vendor == 0 && device != 0)) {
95*32640292SAndy Fiddaman 		warnx("pci_hostbridge: must specify both vendor and "
964c87aefeSPatrick Mooney 		    "device for custom hostbridge");
974c87aefeSPatrick Mooney 		return (-1);
984c87aefeSPatrick Mooney 	}
992b948146SAndy Fiddaman 	if (model == NULL && vendor == 0 && device == 0)
1002b948146SAndy Fiddaman 		model = "netapp";
1014c87aefeSPatrick Mooney 
1022b948146SAndy Fiddaman 	if (model != NULL) {
1032b948146SAndy Fiddaman 		for (uint_t i = 0; i < NUM_HB_MODELS; i++) {
1044c87aefeSPatrick Mooney 			if (strcmp(model, pci_hb_models[i].phm_model) != 0)
1054c87aefeSPatrick Mooney 				continue;
1064c87aefeSPatrick Mooney 
1074c87aefeSPatrick Mooney 			/* found a model match */
1082b948146SAndy Fiddaman 			vendor = pci_hb_models[i].phm_vendor;
1092b948146SAndy Fiddaman 			device = pci_hb_models[i].phm_device;
1102b948146SAndy Fiddaman 			break;
1114c87aefeSPatrick Mooney 		}
1122b948146SAndy Fiddaman 		if (vendor == 0) {
113*32640292SAndy Fiddaman 			warnx("pci_hostbridge: invalid model '%s'", model);
1144c87aefeSPatrick Mooney 			return (-1);
1154c87aefeSPatrick Mooney 		}
1162b948146SAndy Fiddaman 	}
1179e6c6f2fSPatrick Mooney 
1189e6c6f2fSPatrick Mooney 	/* Both i440fx and Q35 chipsets feature the concept of Programmable
1199e6c6f2fSPatrick Mooney 	 * Address Memory (PAM), where certain physical address ranges can be
1209e6c6f2fSPatrick Mooney 	 * configured to direct reads/writes to either DRAM, or to the PCI MMIO
1219e6c6f2fSPatrick Mooney 	 * space instead.  At boot, they default to bypassing DRAM, so in order
1229e6c6f2fSPatrick Mooney 	 * to cheaply paper over our lack of emulation, the memory in PAM0
1239e6c6f2fSPatrick Mooney 	 * (0xf0000-0xfffff, the System BIOS segment) should be zeroed.
1249e6c6f2fSPatrick Mooney 	 *
1259e6c6f2fSPatrick Mooney 	 * If this emulation is expanded in the future to truly support PAM
1269e6c6f2fSPatrick Mooney 	 * behavior, this hack can be removed.
1279e6c6f2fSPatrick Mooney 	 */
1289e6c6f2fSPatrick Mooney 	if (vendor == 0x8086 && (device == 0x1237 || device == 0x29b0)) {
1299e6c6f2fSPatrick Mooney 		const uintptr_t start = 0xf0000;
1309e6c6f2fSPatrick Mooney 		const size_t len = 0x10000;
131*32640292SAndy Fiddaman 		void *system_bios_region = paddr_guest2host(pi->pi_vmctx,
132*32640292SAndy Fiddaman 		    start, len);
1339e6c6f2fSPatrick Mooney 		assert(system_bios_region != NULL);
1349e6c6f2fSPatrick Mooney 		bzero(system_bios_region, len);
1359e6c6f2fSPatrick Mooney 	}
1362b948146SAndy Fiddaman #endif /* !__FreeBSD__ */
1374c87aefeSPatrick Mooney 
1382b948146SAndy Fiddaman 	/* config space */
1392b948146SAndy Fiddaman 	pci_set_cfgdata16(pi, PCIR_VENDOR, vendor);
1402b948146SAndy Fiddaman 	pci_set_cfgdata16(pi, PCIR_DEVICE, device);
1412b948146SAndy Fiddaman 	pci_set_cfgdata8(pi, PCIR_HDRTYPE, PCIM_HDRTYPE_NORMAL);
1422b948146SAndy Fiddaman 	pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_BRIDGE);
1432b948146SAndy Fiddaman 	pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_BRIDGE_HOST);
1442b948146SAndy Fiddaman 
1452b948146SAndy Fiddaman 	pci_emul_add_pciecap(pi, PCIEM_TYPE_ROOT_PORT);
1462b948146SAndy Fiddaman 
1474c87aefeSPatrick Mooney 	return (0);
1484c87aefeSPatrick Mooney }
1494c87aefeSPatrick Mooney 
1504c87aefeSPatrick Mooney static int
pci_amd_hostbridge_legacy_config(nvlist_t * nvl,const char * opts __unused)15159d65d31SAndy Fiddaman pci_amd_hostbridge_legacy_config(nvlist_t *nvl, const char *opts __unused)
1524c87aefeSPatrick Mooney {
153*32640292SAndy Fiddaman 	nvlist_t *pci_regs;
154*32640292SAndy Fiddaman 
155*32640292SAndy Fiddaman 	pci_regs = create_relative_config_node(nvl, "pcireg");
156*32640292SAndy Fiddaman 	if (pci_regs == NULL) {
157*32640292SAndy Fiddaman 		warnx("amd_hostbridge: failed to create pciregs node");
158*32640292SAndy Fiddaman 		return (-1);
159*32640292SAndy Fiddaman 	}
160*32640292SAndy Fiddaman 	set_config_value_node(pci_regs, "vendor", "0x1022");	/* AMD */
161*32640292SAndy Fiddaman 	set_config_value_node(pci_regs, "device", "0x7432");	/* made up */
1624c87aefeSPatrick Mooney 
1634c87aefeSPatrick Mooney 	return (0);
1644c87aefeSPatrick Mooney }
1654c87aefeSPatrick Mooney 
1664f3f3e9aSAndy Fiddaman static const struct pci_devemu pci_de_amd_hostbridge = {
167bf21cd93STycho Nightingale 	.pe_emu = "amd_hostbridge",
1682b948146SAndy Fiddaman 	.pe_legacy_config = pci_amd_hostbridge_legacy_config,
1692b948146SAndy Fiddaman 	.pe_alias = "hostbridge",
170bf21cd93STycho Nightingale };
171bf21cd93STycho Nightingale PCI_EMUL_SET(pci_de_amd_hostbridge);
172bf21cd93STycho Nightingale 
1734f3f3e9aSAndy Fiddaman static const struct pci_devemu pci_de_hostbridge = {
174bf21cd93STycho Nightingale 	.pe_emu = "hostbridge",
175bf21cd93STycho Nightingale 	.pe_init = pci_hostbridge_init,
176bf21cd93STycho Nightingale };
177bf21cd93STycho Nightingale PCI_EMUL_SET(pci_de_hostbridge);
178