xref: /illumos-gate/usr/src/uts/i86pc/io/amd_iommu/amd_iommu.c (revision 6732dbb379bf754b70168b01ba56793737f9f3e7)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/file.h>
29 #include <sys/errno.h>
30 #include <sys/open.h>
31 #include <sys/stat.h>
32 #include <sys/cred.h>
33 #include <sys/modctl.h>
34 #include <sys/conf.h>
35 #include <sys/devops.h>
36 #include <sys/ddi.h>
37 #include <sys/x86_archext.h>
38 
39 #include <sys/amd_iommu.h>
40 #include "amd_iommu_impl.h"
41 #include "amd_iommu_acpi.h"
42 
43 
44 #define	AMD_IOMMU_MINOR2INST(x)	(x)
45 #define	AMD_IOMMU_INST2MINOR(x)	(x)
46 #define	AMD_IOMMU_NODETYPE	"ddi_iommu"
47 #define	AMD_IOMMU_MINOR_NAME	"amd-iommu"
48 
49 static int amd_iommu_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
50     void **result);
51 static int amd_iommu_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
52 static int amd_iommu_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
53 static int amd_iommu_open(dev_t *devp, int flag, int otyp, cred_t *credp);
54 static int amd_iommu_close(dev_t dev, int flag, int otyp, cred_t *credp);
55 static int amd_iommu_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
56     cred_t *credp, int *rvalp);
57 
58 static struct cb_ops amd_iommu_cb_ops = {
59 	amd_iommu_open,		/* cb_open */
60 	amd_iommu_close,	/* cb_close */
61 	nodev,			/* cb_strategy */
62 	nodev,			/* cb_print */
63 	nodev,			/* cb_dump */
64 	nodev,			/* cb_read */
65 	nodev,			/* cb_write */
66 	amd_iommu_ioctl,	/* cb_ioctl */
67 	nodev,			/* cb_devmap */
68 	nodev,			/* cb_mmap */
69 	nodev,			/* cb_segmap */
70 	nochpoll,		/* cb_chpoll */
71 	ddi_prop_op,		/* cb_prop_op */
72 	NULL,			/* cb_str */
73 	D_NEW | D_MP,		/* cb_flag */
74 	CB_REV,			/* cb_rev */
75 	nodev,			/* cb_aread */
76 	nodev			/* cb_awrite */
77 };
78 
79 static struct dev_ops amd_iommu_dev_ops = {
80 	DEVO_REV,		/* devo_rev */
81 	0,			/* devo_refcnt */
82 	amd_iommu_getinfo,	/* devo_getinfo */
83 	nulldev,		/* devo_identify */
84 	nulldev,		/* devo_probe */
85 	amd_iommu_attach,	/* devo_attach */
86 	amd_iommu_detach,	/* devo_detach */
87 	nodev,			/* devo_reset */
88 	&amd_iommu_cb_ops,	/* devo_cb_ops */
89 	NULL,			/* devo_bus_ops */
90 	nulldev			/* devo_power */
91 };
92 
93 static struct modldrv modldrv = {
94 	&mod_driverops,
95 	"AMD IOMMU 0.1",
96 	&amd_iommu_dev_ops
97 };
98 
99 static struct modlinkage modlinkage = {
100 	MODREV_1,
101 	(void *)&modldrv,
102 	NULL
103 };
104 
105 amd_iommu_debug_t amd_iommu_debug;
106 kmutex_t amd_iommu_global_lock;
107 const char *amd_iommu_modname = "amd_iommu";
108 amd_iommu_alias_t **amd_iommu_alias;
109 amd_iommu_page_table_hash_t amd_iommu_page_table_hash;
110 static void *amd_iommu_statep;
111 int amd_iommu_64bit_bug;
112 int amd_iommu_unity_map;
113 int amd_iommu_no_RW_perms;
114 int amd_iommu_no_unmap;
115 int amd_iommu_pageva_inval_all;
116 int amd_iommu_disable;		/* disable IOMMU */
117 char *amd_iommu_disable_list;	/* list of drivers bypassing IOMMU */
118 
119 int
120 _init(void)
121 {
122 	int error = ENOTSUP;
123 
124 #if defined(__amd64) && !defined(__xpv)
125 
126 	if (get_hwenv() != HW_NATIVE)
127 		return (ENOTSUP);
128 
129 	error = ddi_soft_state_init(&amd_iommu_statep,
130 	    sizeof (struct amd_iommu_state), 1);
131 	if (error) {
132 		cmn_err(CE_WARN, "%s: _init: failed to init soft state.",
133 		    amd_iommu_modname);
134 		return (error);
135 	}
136 
137 	if (amd_iommu_acpi_init() != DDI_SUCCESS) {
138 		if (amd_iommu_debug) {
139 			cmn_err(CE_WARN, "%s: _init: ACPI init failed.",
140 			    amd_iommu_modname);
141 		}
142 		ddi_soft_state_fini(&amd_iommu_statep);
143 		return (ENOTSUP);
144 	}
145 
146 	amd_iommu_read_boot_props();
147 
148 	if (amd_iommu_page_table_hash_init(&amd_iommu_page_table_hash)
149 	    != DDI_SUCCESS) {
150 		cmn_err(CE_WARN, "%s: _init: Page table hash init failed.",
151 		    amd_iommu_modname);
152 		if (amd_iommu_disable_list) {
153 			kmem_free(amd_iommu_disable_list,
154 			    strlen(amd_iommu_disable_list) + 1);
155 			amd_iommu_disable_list = NULL;
156 		}
157 		amd_iommu_acpi_fini();
158 		ddi_soft_state_fini(&amd_iommu_statep);
159 		amd_iommu_statep = NULL;
160 		return (EFAULT);
161 	}
162 
163 	error = mod_install(&modlinkage);
164 	if (error) {
165 		cmn_err(CE_WARN, "%s: _init: mod_install failed.",
166 		    amd_iommu_modname);
167 		amd_iommu_page_table_hash_fini(&amd_iommu_page_table_hash);
168 		if (amd_iommu_disable_list) {
169 			kmem_free(amd_iommu_disable_list,
170 			    strlen(amd_iommu_disable_list) + 1);
171 			amd_iommu_disable_list = NULL;
172 		}
173 		amd_iommu_acpi_fini();
174 		ddi_soft_state_fini(&amd_iommu_statep);
175 		amd_iommu_statep = NULL;
176 		return (error);
177 	}
178 	error = 0;
179 #endif
180 
181 	return (error);
182 }
183 
184 int
185 _info(struct modinfo *modinfop)
186 {
187 	return (mod_info(&modlinkage, modinfop));
188 }
189 
190 int
191 _fini(void)
192 {
193 	int error;
194 
195 	error = mod_remove(&modlinkage);
196 	if (error)
197 		return (error);
198 
199 	amd_iommu_page_table_hash_fini(&amd_iommu_page_table_hash);
200 	if (amd_iommu_disable_list) {
201 		kmem_free(amd_iommu_disable_list,
202 		    strlen(amd_iommu_disable_list) + 1);
203 		amd_iommu_disable_list = NULL;
204 	}
205 	amd_iommu_acpi_fini();
206 	ddi_soft_state_fini(&amd_iommu_statep);
207 	amd_iommu_statep = NULL;
208 
209 	return (0);
210 }
211 
212 /*ARGSUSED*/
213 static int
214 amd_iommu_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
215 {
216 	struct amd_iommu_state *statep;
217 
218 	ASSERT(result);
219 
220 	*result = NULL;
221 
222 	switch (cmd) {
223 	case DDI_INFO_DEVT2DEVINFO:
224 		statep = ddi_get_soft_state(amd_iommu_statep,
225 		    AMD_IOMMU_MINOR2INST(getminor((dev_t)arg)));
226 		if (statep) {
227 			*result = statep->aioms_devi;
228 			return (DDI_SUCCESS);
229 		}
230 		break;
231 	case DDI_INFO_DEVT2INSTANCE:
232 		*result = (void *)(uintptr_t)
233 		    AMD_IOMMU_MINOR2INST(getminor((dev_t)arg));
234 		return (DDI_SUCCESS);
235 	}
236 
237 	return (DDI_FAILURE);
238 }
239 
240 static int
241 amd_iommu_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
242 {
243 	int instance = ddi_get_instance(dip);
244 	const char *driver = ddi_driver_name(dip);
245 	struct amd_iommu_state *statep;
246 
247 	ASSERT(instance >= 0);
248 	ASSERT(driver);
249 
250 	switch (cmd) {
251 	case DDI_ATTACH:
252 		if (ddi_soft_state_zalloc(amd_iommu_statep, instance)
253 		    != DDI_SUCCESS) {
254 			cmn_err(CE_WARN, "Unable to allocate soft state for "
255 			    "%s%d", driver, instance);
256 			return (DDI_FAILURE);
257 		}
258 
259 		statep = ddi_get_soft_state(amd_iommu_statep, instance);
260 		if (statep == NULL) {
261 			cmn_err(CE_WARN, "Unable to get soft state for "
262 			    "%s%d", driver, instance);
263 			ddi_soft_state_free(amd_iommu_statep, instance);
264 			return (DDI_FAILURE);
265 		}
266 
267 		if (ddi_create_minor_node(dip, AMD_IOMMU_MINOR_NAME, S_IFCHR,
268 		    AMD_IOMMU_INST2MINOR(instance), AMD_IOMMU_NODETYPE,
269 		    0) != DDI_SUCCESS) {
270 			cmn_err(CE_WARN, "Unable to create minor node for "
271 			    "%s%d", driver, instance);
272 			ddi_remove_minor_node(dip, NULL);
273 			ddi_soft_state_free(amd_iommu_statep, instance);
274 			return (DDI_FAILURE);
275 		}
276 
277 		statep->aioms_devi = dip;
278 		statep->aioms_instance = instance;
279 		statep->aioms_iommu_start = NULL;
280 		statep->aioms_iommu_end = NULL;
281 
282 		amd_iommu_lookup_conf_props(dip);
283 
284 		if (amd_iommu_disable_list) {
285 			cmn_err(CE_NOTE, "AMD IOMMU disabled for the following"
286 			    " drivers:\n%s", amd_iommu_disable_list);
287 		}
288 
289 		if (amd_iommu_disable) {
290 			cmn_err(CE_NOTE, "AMD IOMMU disabled by user");
291 		} else if (amd_iommu_setup(dip, statep) != DDI_SUCCESS) {
292 			cmn_err(CE_WARN, "Unable to initialize AMD IOMMU "
293 			    "%s%d", driver, instance);
294 			ddi_remove_minor_node(dip, NULL);
295 			ddi_soft_state_free(amd_iommu_statep, instance);
296 			return (DDI_FAILURE);
297 		}
298 
299 		ddi_report_dev(dip);
300 
301 		return (DDI_SUCCESS);
302 
303 	case DDI_RESUME:
304 		return (DDI_SUCCESS);
305 	default:
306 		return (DDI_FAILURE);
307 	}
308 }
309 
310 static int
311 amd_iommu_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
312 {
313 	int instance = ddi_get_instance(dip);
314 	const char *driver = ddi_driver_name(dip);
315 	struct amd_iommu_state *statep;
316 
317 	ASSERT(instance >= 0);
318 	ASSERT(driver);
319 
320 	switch (cmd) {
321 	case DDI_DETACH:
322 		statep = ddi_get_soft_state(amd_iommu_statep, instance);
323 		if (statep == NULL) {
324 			cmn_err(CE_WARN, "%s%d: Cannot get soft state",
325 			    driver, instance);
326 			return (DDI_FAILURE);
327 		}
328 		return (DDI_FAILURE);
329 	case DDI_SUSPEND:
330 		return (DDI_SUCCESS);
331 	default:
332 		return (DDI_FAILURE);
333 	}
334 }
335 
336 /*ARGSUSED*/
337 static int
338 amd_iommu_open(dev_t *devp, int flag, int otyp, cred_t *credp)
339 {
340 	int instance = AMD_IOMMU_MINOR2INST(getminor(*devp));
341 	struct amd_iommu_state *statep;
342 	const char *f = "amd_iommu_open";
343 
344 	if (instance < 0) {
345 		cmn_err(CE_WARN, "%s: invalid instance %d",
346 		    f, instance);
347 		return (ENXIO);
348 	}
349 
350 	if (!(flag & (FREAD|FWRITE))) {
351 		cmn_err(CE_WARN, "%s: invalid flags %d", f, flag);
352 		return (EINVAL);
353 	}
354 
355 	if (otyp != OTYP_CHR) {
356 		cmn_err(CE_WARN, "%s: invalid otyp %d", f, otyp);
357 		return (EINVAL);
358 	}
359 
360 	statep = ddi_get_soft_state(amd_iommu_statep, instance);
361 	if (statep == NULL) {
362 		cmn_err(CE_WARN, "%s: cannot get soft state: instance %d",
363 		    f, instance);
364 		return (ENXIO);
365 	}
366 
367 	ASSERT(statep->aioms_instance == instance);
368 
369 	return (0);
370 }
371 
372 /*ARGSUSED*/
373 static int
374 amd_iommu_close(dev_t dev, int flag, int otyp, cred_t *credp)
375 {
376 	int instance = AMD_IOMMU_MINOR2INST(getminor(dev));
377 	struct amd_iommu_state *statep;
378 	const char *f = "amd_iommu_close";
379 
380 	if (instance < 0) {
381 		cmn_err(CE_WARN, "%s: invalid instance %d", f, instance);
382 		return (ENXIO);
383 	}
384 
385 	if (!(flag & (FREAD|FWRITE))) {
386 		cmn_err(CE_WARN, "%s: invalid flags %d", f, flag);
387 		return (EINVAL);
388 	}
389 
390 	if (otyp != OTYP_CHR) {
391 		cmn_err(CE_WARN, "%s: invalid otyp %d", f, otyp);
392 		return (EINVAL);
393 	}
394 
395 	statep = ddi_get_soft_state(amd_iommu_statep, instance);
396 	if (statep == NULL) {
397 		cmn_err(CE_WARN, "%s: cannot get soft state: instance %d",
398 		    f, instance);
399 		return (ENXIO);
400 	}
401 
402 	ASSERT(statep->aioms_instance == instance);
403 	return (0);
404 
405 }
406 
407 /*ARGSUSED*/
408 static int
409 amd_iommu_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
410     int *rvalp)
411 {
412 	int instance = AMD_IOMMU_MINOR2INST(getminor(dev));
413 	struct amd_iommu_state *statep;
414 	const char *f = "amd_iommu_ioctl";
415 
416 	ASSERT(*rvalp);
417 
418 	if (instance < 0) {
419 		cmn_err(CE_WARN, "%s: invalid instance %d", f, instance);
420 		return (ENXIO);
421 	}
422 
423 
424 	if (!(mode & (FREAD|FWRITE))) {
425 		cmn_err(CE_WARN, "%s: invalid mode %d", f, mode);
426 		return (EINVAL);
427 	}
428 
429 	if (mode & FKIOCTL) {
430 		cmn_err(CE_WARN, "%s: FKIOCTL unsupported mode %d", f, mode);
431 		return (EINVAL);
432 	}
433 
434 	statep = ddi_get_soft_state(amd_iommu_statep, instance);
435 	if (statep == NULL) {
436 		cmn_err(CE_WARN, "%s: cannot get soft state: instance %d",
437 		    f, instance);
438 		return (ENXIO);
439 	}
440 
441 	ASSERT(statep->aioms_instance == instance);
442 
443 	return (ENOTTY);
444 }
445