xref: /illumos-gate/usr/src/cmd/bhyve/pci_hostbridge.c (revision 4e065a9f6a4471f1001853cd10a093bc5beb58a5)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2011 NetApp, Inc.
5  * Copyright (c) 2018 Joyent, Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef __FreeBSD__
34 #include <errno.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <strings.h>
38 #endif
39 __FBSDID("$FreeBSD$");
40 
41 #include "pci_emul.h"
42 
43 #ifdef __FreeBSD__
44 static int
45 pci_hostbridge_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
46 {
47 
48 	/* config space */
49 	pci_set_cfgdata16(pi, PCIR_VENDOR, 0x1275);	/* NetApp */
50 	pci_set_cfgdata16(pi, PCIR_DEVICE, 0x1275);	/* NetApp */
51 	pci_set_cfgdata8(pi, PCIR_HDRTYPE, PCIM_HDRTYPE_NORMAL);
52 	pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_BRIDGE);
53 	pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_BRIDGE_HOST);
54 
55 	pci_emul_add_pciecap(pi, PCIEM_TYPE_ROOT_PORT);
56 
57 	return (0);
58 }
59 
60 static int
61 pci_amd_hostbridge_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
62 {
63 	(void) pci_hostbridge_init(ctx, pi, opts);
64 	pci_set_cfgdata16(pi, PCIR_VENDOR, 0x1022);	/* AMD */
65 	pci_set_cfgdata16(pi, PCIR_DEVICE, 0x7432);	/* made up */
66 
67 	return (0);
68 }
69 #else
70 static void
71 pci_hostbridge_setup(struct pci_devinst *pi, uint16_t vendor, uint16_t device)
72 {
73 	/* config space */
74 	pci_set_cfgdata16(pi, PCIR_VENDOR, vendor);
75 	pci_set_cfgdata16(pi, PCIR_DEVICE, device);
76 	pci_set_cfgdata8(pi, PCIR_HDRTYPE, PCIM_HDRTYPE_NORMAL);
77 	pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_BRIDGE);
78 	pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_BRIDGE_HOST);
79 
80 	pci_emul_add_pciecap(pi, PCIEM_TYPE_ROOT_PORT);
81 }
82 
83 
84 static int
85 pci_hostbridge_parse_pci_val(const char *in, uint16_t *val)
86 {
87 	long num;
88 	char *endp = NULL;
89 
90 	errno = 0;
91 	num = strtol(in, &endp, 0);
92 	if (errno != 0 || endp == NULL || *endp != '\0') {
93 		fprintf(stderr, "pci_hostbridge: invalid num '%s'", in);
94 		return (-1);
95 	} else if (num < 1 || num > UINT16_MAX) {
96 		fprintf(stderr, "pci_hostbridge: 0x%04lx out of range", num);
97 		return (-1);
98 	}
99 	*val = num;
100 	return (0);
101 }
102 
103 static struct pci_hostbridge_model {
104 	const char	*phm_model;
105 	uint16_t	phm_vendor;
106 	uint16_t	phm_device;
107 } pci_hb_models[] = {
108 	{ "amd",	0x1022, 0x7432 }, /* AMD/made-up */
109 	{ "netapp",	0x1275, 0x1275 }, /* NetApp/NetApp */
110 	{ "i440fx",	0x8086, 0x1237 }, /* Intel/82441 */
111 	{ "q35",	0x8086, 0x29b0 }, /* Intel/Q35 HB */
112 };
113 
114 #define	NUM_HB_MODELS	(sizeof (pci_hb_models) / sizeof (pci_hb_models[0]))
115 
116 static int
117 pci_hostbridge_parse_args(char *opts, uint16_t *vendorp, uint16_t *devicep)
118 {
119 	const char *model = NULL;
120 	char *next;
121 	uint16_t vendor = 0, device = 0;
122 	int err = 0;
123 
124 	for (; opts != NULL && *opts != '\0'; opts = next) {
125 		char *val, *cp;
126 
127 		if ((cp = strchr(opts, ',')) != NULL) {
128 			*cp = '\0';
129 			next = cp + 1;
130 		} else {
131 			next = NULL;
132 		}
133 
134 		if ((cp = strchr(opts, '=')) == NULL) {
135 			fprintf(stderr,
136 			    "pci_hostbridge: expected value for param"
137 			    " (%s=VAL)", opts);
138 			err = -1;
139 			continue;
140 		}
141 
142 		/* <param>=<value> handling */
143 		val = cp + 1;
144 		*cp = '\0';
145 		if (strcmp(opts, "model") == 0) {
146 			model = val;
147 		} else if (strcmp(opts, "vendor") == 0) {
148 			if (pci_hostbridge_parse_pci_val(val, &vendor) != 0) {
149 				err = -1;
150 				continue;
151 			}
152 		} else if (strcmp(opts, "device") == 0) {
153 			if (pci_hostbridge_parse_pci_val(val, &device) != 0) {
154 				err = -1;
155 				continue;
156 			}
157 		} else {
158 			fprintf(stderr,
159 			    "pci_hostbridge: unrecognized option '%s'", opts);
160 			err = -1;
161 			continue;
162 		}
163 	}
164 	if (err != 0) {
165 		return (err);
166 	}
167 
168 	if (model != NULL && (vendor != 0 || device != 0)) {
169 		fprintf(stderr, "pci_hostbridge: cannot specify model "
170 		    "and vendor/device");
171 		return (-1);
172 	} else if ((vendor != 0 && device == 0) ||
173 	    (vendor == 0 && device != 0)) {
174 		fprintf(stderr, "pci_hostbridge: must specify both vendor and"
175 		    "device for custom hostbridge");
176 		return (-1);
177 	}
178 	if (model != NULL) {
179 		uint_t i;
180 
181 		for (i = 0; i < NUM_HB_MODELS; i++) {
182 			if (strcmp(model, pci_hb_models[i].phm_model) != 0)
183 				continue;
184 
185 			/* found a model match */
186 			*vendorp = pci_hb_models[i].phm_vendor;
187 			*devicep = pci_hb_models[i].phm_device;
188 			return (0);
189 		}
190 		fprintf(stderr, "pci_hostbridge: invalid model '%s'", model);
191 		return (-1);
192 	}
193 
194 	/* custom hostbridge ID was specified */
195 	*vendorp = vendor;
196 	*devicep = device;
197 	return (0);
198 }
199 
200 static int
201 pci_hostbridge_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
202 {
203 	uint16_t vendor, device;
204 
205 	if (opts == NULL) {
206 		/* Fall back to NetApp default if no options are specified */
207 		vendor = 0x1275;
208 		device = 0x1275;
209 	} else if (pci_hostbridge_parse_args(opts, &vendor, &device) != 0) {
210 		return (-1);
211 	}
212 
213 	pci_hostbridge_setup(pi, vendor, device);
214 	return (0);
215 }
216 
217 static int
218 pci_amd_hostbridge_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
219 {
220 	pci_hostbridge_setup(pi, 0x1022, 0x7432);
221 	return (0);
222 }
223 
224 #endif /* __FreeBSD__ */
225 
226 struct pci_devemu pci_de_amd_hostbridge = {
227 	.pe_emu = "amd_hostbridge",
228 	.pe_init = pci_amd_hostbridge_init,
229 };
230 PCI_EMUL_SET(pci_de_amd_hostbridge);
231 
232 struct pci_devemu pci_de_hostbridge = {
233 	.pe_emu = "hostbridge",
234 	.pe_init = pci_hostbridge_init,
235 };
236 PCI_EMUL_SET(pci_de_hostbridge);
237