xref: /illumos-gate/usr/src/uts/i86pc/io/amd_iommu/amd_iommu.c (revision 89b2a9fbeabf42fa54594df0e5927bcc50a07cc9)
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 static int amd_iommu_quiesce(dev_info_t *dip);
58 
59 static struct cb_ops amd_iommu_cb_ops = {
60 	amd_iommu_open,		/* cb_open */
61 	amd_iommu_close,	/* cb_close */
62 	nodev,			/* cb_strategy */
63 	nodev,			/* cb_print */
64 	nodev,			/* cb_dump */
65 	nodev,			/* cb_read */
66 	nodev,			/* cb_write */
67 	amd_iommu_ioctl,	/* cb_ioctl */
68 	nodev,			/* cb_devmap */
69 	nodev,			/* cb_mmap */
70 	nodev,			/* cb_segmap */
71 	nochpoll,		/* cb_chpoll */
72 	ddi_prop_op,		/* cb_prop_op */
73 	NULL,			/* cb_str */
74 	D_NEW | D_MP,		/* cb_flag */
75 	CB_REV,			/* cb_rev */
76 	nodev,			/* cb_aread */
77 	nodev			/* cb_awrite */
78 };
79 
80 static struct dev_ops amd_iommu_dev_ops = {
81 	DEVO_REV,		/* devo_rev */
82 	0,			/* devo_refcnt */
83 	amd_iommu_getinfo,	/* devo_getinfo */
84 	nulldev,		/* devo_identify */
85 	nulldev,		/* devo_probe */
86 	amd_iommu_attach,	/* devo_attach */
87 	amd_iommu_detach,	/* devo_detach */
88 	nodev,			/* devo_reset */
89 	&amd_iommu_cb_ops,	/* devo_cb_ops */
90 	NULL,			/* devo_bus_ops */
91 	nulldev,		/* devo_power */
92 	amd_iommu_quiesce,	/* devo_quiesce */
93 };
94 
95 static struct modldrv modldrv = {
96 	&mod_driverops,
97 	"AMD IOMMU 0.1",
98 	&amd_iommu_dev_ops
99 };
100 
101 static struct modlinkage modlinkage = {
102 	MODREV_1,
103 	(void *)&modldrv,
104 	NULL
105 };
106 
107 amd_iommu_debug_t amd_iommu_debug;
108 kmutex_t amd_iommu_global_lock;
109 const char *amd_iommu_modname = "amd_iommu";
110 amd_iommu_alias_t **amd_iommu_alias;
111 amd_iommu_page_table_hash_t amd_iommu_page_table_hash;
112 static void *amd_iommu_statep;
113 int amd_iommu_64bit_bug;
114 int amd_iommu_unity_map;
115 int amd_iommu_no_RW_perms;
116 int amd_iommu_no_unmap;
117 int amd_iommu_pageva_inval_all;
118 int amd_iommu_disable;		/* disable IOMMU */
119 char *amd_iommu_disable_list;	/* list of drivers bypassing IOMMU */
120 
121 int
122 _init(void)
123 {
124 	int error = ENOTSUP;
125 
126 #if defined(__amd64) && !defined(__xpv)
127 
128 	if (get_hwenv() != HW_NATIVE)
129 		return (ENOTSUP);
130 
131 	error = ddi_soft_state_init(&amd_iommu_statep,
132 	    sizeof (struct amd_iommu_state), 1);
133 	if (error) {
134 		cmn_err(CE_WARN, "%s: _init: failed to init soft state.",
135 		    amd_iommu_modname);
136 		return (error);
137 	}
138 
139 	if (amd_iommu_acpi_init() != DDI_SUCCESS) {
140 		if (amd_iommu_debug) {
141 			cmn_err(CE_WARN, "%s: _init: ACPI init failed.",
142 			    amd_iommu_modname);
143 		}
144 		ddi_soft_state_fini(&amd_iommu_statep);
145 		return (ENOTSUP);
146 	}
147 
148 	amd_iommu_read_boot_props();
149 
150 	if (amd_iommu_page_table_hash_init(&amd_iommu_page_table_hash)
151 	    != DDI_SUCCESS) {
152 		cmn_err(CE_WARN, "%s: _init: Page table hash init failed.",
153 		    amd_iommu_modname);
154 		if (amd_iommu_disable_list) {
155 			kmem_free(amd_iommu_disable_list,
156 			    strlen(amd_iommu_disable_list) + 1);
157 			amd_iommu_disable_list = NULL;
158 		}
159 		amd_iommu_acpi_fini();
160 		ddi_soft_state_fini(&amd_iommu_statep);
161 		amd_iommu_statep = NULL;
162 		return (EFAULT);
163 	}
164 
165 	error = mod_install(&modlinkage);
166 	if (error) {
167 		cmn_err(CE_WARN, "%s: _init: mod_install failed.",
168 		    amd_iommu_modname);
169 		amd_iommu_page_table_hash_fini(&amd_iommu_page_table_hash);
170 		if (amd_iommu_disable_list) {
171 			kmem_free(amd_iommu_disable_list,
172 			    strlen(amd_iommu_disable_list) + 1);
173 			amd_iommu_disable_list = NULL;
174 		}
175 		amd_iommu_acpi_fini();
176 		ddi_soft_state_fini(&amd_iommu_statep);
177 		amd_iommu_statep = NULL;
178 		return (error);
179 	}
180 	error = 0;
181 #endif
182 
183 	return (error);
184 }
185 
186 int
187 _info(struct modinfo *modinfop)
188 {
189 	return (mod_info(&modlinkage, modinfop));
190 }
191 
192 int
193 _fini(void)
194 {
195 	int error;
196 
197 	error = mod_remove(&modlinkage);
198 	if (error)
199 		return (error);
200 
201 	amd_iommu_page_table_hash_fini(&amd_iommu_page_table_hash);
202 	if (amd_iommu_disable_list) {
203 		kmem_free(amd_iommu_disable_list,
204 		    strlen(amd_iommu_disable_list) + 1);
205 		amd_iommu_disable_list = NULL;
206 	}
207 	amd_iommu_acpi_fini();
208 	ddi_soft_state_fini(&amd_iommu_statep);
209 	amd_iommu_statep = NULL;
210 
211 	return (0);
212 }
213 
214 /*ARGSUSED*/
215 static int
216 amd_iommu_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
217 {
218 	struct amd_iommu_state *statep;
219 
220 	ASSERT(result);
221 
222 	*result = NULL;
223 
224 	switch (cmd) {
225 	case DDI_INFO_DEVT2DEVINFO:
226 		statep = ddi_get_soft_state(amd_iommu_statep,
227 		    AMD_IOMMU_MINOR2INST(getminor((dev_t)arg)));
228 		if (statep) {
229 			*result = statep->aioms_devi;
230 			return (DDI_SUCCESS);
231 		}
232 		break;
233 	case DDI_INFO_DEVT2INSTANCE:
234 		*result = (void *)(uintptr_t)
235 		    AMD_IOMMU_MINOR2INST(getminor((dev_t)arg));
236 		return (DDI_SUCCESS);
237 	}
238 
239 	return (DDI_FAILURE);
240 }
241 
242 static int
243 amd_iommu_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
244 {
245 	int instance = ddi_get_instance(dip);
246 	const char *driver = ddi_driver_name(dip);
247 	struct amd_iommu_state *statep;
248 
249 	ASSERT(instance >= 0);
250 	ASSERT(driver);
251 
252 	switch (cmd) {
253 	case DDI_ATTACH:
254 		if (ddi_soft_state_zalloc(amd_iommu_statep, instance)
255 		    != DDI_SUCCESS) {
256 			cmn_err(CE_WARN, "Unable to allocate soft state for "
257 			    "%s%d", driver, instance);
258 			return (DDI_FAILURE);
259 		}
260 
261 		statep = ddi_get_soft_state(amd_iommu_statep, instance);
262 		if (statep == NULL) {
263 			cmn_err(CE_WARN, "Unable to get soft state for "
264 			    "%s%d", driver, instance);
265 			ddi_soft_state_free(amd_iommu_statep, instance);
266 			return (DDI_FAILURE);
267 		}
268 
269 		if (ddi_create_minor_node(dip, AMD_IOMMU_MINOR_NAME, S_IFCHR,
270 		    AMD_IOMMU_INST2MINOR(instance), AMD_IOMMU_NODETYPE,
271 		    0) != DDI_SUCCESS) {
272 			cmn_err(CE_WARN, "Unable to create minor node for "
273 			    "%s%d", driver, instance);
274 			ddi_remove_minor_node(dip, NULL);
275 			ddi_soft_state_free(amd_iommu_statep, instance);
276 			return (DDI_FAILURE);
277 		}
278 
279 		statep->aioms_devi = dip;
280 		statep->aioms_instance = instance;
281 		statep->aioms_iommu_start = NULL;
282 		statep->aioms_iommu_end = NULL;
283 
284 		amd_iommu_lookup_conf_props(dip);
285 
286 		if (amd_iommu_disable_list) {
287 			cmn_err(CE_NOTE, "AMD IOMMU disabled for the following"
288 			    " drivers:\n%s", amd_iommu_disable_list);
289 		}
290 
291 		if (amd_iommu_disable) {
292 			cmn_err(CE_NOTE, "AMD IOMMU disabled by user");
293 		} else if (amd_iommu_setup(dip, statep) != DDI_SUCCESS) {
294 			cmn_err(CE_WARN, "Unable to initialize AMD IOMMU "
295 			    "%s%d", driver, instance);
296 			ddi_remove_minor_node(dip, NULL);
297 			ddi_soft_state_free(amd_iommu_statep, instance);
298 			return (DDI_FAILURE);
299 		}
300 
301 		ddi_report_dev(dip);
302 
303 		return (DDI_SUCCESS);
304 
305 	case DDI_RESUME:
306 		return (DDI_SUCCESS);
307 	default:
308 		return (DDI_FAILURE);
309 	}
310 }
311 
312 static int
313 amd_iommu_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
314 {
315 	int instance = ddi_get_instance(dip);
316 	const char *driver = ddi_driver_name(dip);
317 	struct amd_iommu_state *statep;
318 
319 	ASSERT(instance >= 0);
320 	ASSERT(driver);
321 
322 	switch (cmd) {
323 	case DDI_DETACH:
324 		statep = ddi_get_soft_state(amd_iommu_statep, instance);
325 		if (statep == NULL) {
326 			cmn_err(CE_WARN, "%s%d: Cannot get soft state",
327 			    driver, instance);
328 			return (DDI_FAILURE);
329 		}
330 		return (DDI_FAILURE);
331 	case DDI_SUSPEND:
332 		return (DDI_SUCCESS);
333 	default:
334 		return (DDI_FAILURE);
335 	}
336 }
337 
338 /*ARGSUSED*/
339 static int
340 amd_iommu_open(dev_t *devp, int flag, int otyp, cred_t *credp)
341 {
342 	int instance = AMD_IOMMU_MINOR2INST(getminor(*devp));
343 	struct amd_iommu_state *statep;
344 	const char *f = "amd_iommu_open";
345 
346 	if (instance < 0) {
347 		cmn_err(CE_WARN, "%s: invalid instance %d",
348 		    f, instance);
349 		return (ENXIO);
350 	}
351 
352 	if (!(flag & (FREAD|FWRITE))) {
353 		cmn_err(CE_WARN, "%s: invalid flags %d", f, flag);
354 		return (EINVAL);
355 	}
356 
357 	if (otyp != OTYP_CHR) {
358 		cmn_err(CE_WARN, "%s: invalid otyp %d", f, otyp);
359 		return (EINVAL);
360 	}
361 
362 	statep = ddi_get_soft_state(amd_iommu_statep, instance);
363 	if (statep == NULL) {
364 		cmn_err(CE_WARN, "%s: cannot get soft state: instance %d",
365 		    f, instance);
366 		return (ENXIO);
367 	}
368 
369 	ASSERT(statep->aioms_instance == instance);
370 
371 	return (0);
372 }
373 
374 /*ARGSUSED*/
375 static int
376 amd_iommu_close(dev_t dev, int flag, int otyp, cred_t *credp)
377 {
378 	int instance = AMD_IOMMU_MINOR2INST(getminor(dev));
379 	struct amd_iommu_state *statep;
380 	const char *f = "amd_iommu_close";
381 
382 	if (instance < 0) {
383 		cmn_err(CE_WARN, "%s: invalid instance %d", f, instance);
384 		return (ENXIO);
385 	}
386 
387 	if (!(flag & (FREAD|FWRITE))) {
388 		cmn_err(CE_WARN, "%s: invalid flags %d", f, flag);
389 		return (EINVAL);
390 	}
391 
392 	if (otyp != OTYP_CHR) {
393 		cmn_err(CE_WARN, "%s: invalid otyp %d", f, otyp);
394 		return (EINVAL);
395 	}
396 
397 	statep = ddi_get_soft_state(amd_iommu_statep, instance);
398 	if (statep == NULL) {
399 		cmn_err(CE_WARN, "%s: cannot get soft state: instance %d",
400 		    f, instance);
401 		return (ENXIO);
402 	}
403 
404 	ASSERT(statep->aioms_instance == instance);
405 	return (0);
406 
407 }
408 
409 /*ARGSUSED*/
410 static int
411 amd_iommu_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
412     int *rvalp)
413 {
414 	int instance = AMD_IOMMU_MINOR2INST(getminor(dev));
415 	struct amd_iommu_state *statep;
416 	const char *f = "amd_iommu_ioctl";
417 
418 	ASSERT(*rvalp);
419 
420 	if (instance < 0) {
421 		cmn_err(CE_WARN, "%s: invalid instance %d", f, instance);
422 		return (ENXIO);
423 	}
424 
425 
426 	if (!(mode & (FREAD|FWRITE))) {
427 		cmn_err(CE_WARN, "%s: invalid mode %d", f, mode);
428 		return (EINVAL);
429 	}
430 
431 	if (mode & FKIOCTL) {
432 		cmn_err(CE_WARN, "%s: FKIOCTL unsupported mode %d", f, mode);
433 		return (EINVAL);
434 	}
435 
436 	statep = ddi_get_soft_state(amd_iommu_statep, instance);
437 	if (statep == NULL) {
438 		cmn_err(CE_WARN, "%s: cannot get soft state: instance %d",
439 		    f, instance);
440 		return (ENXIO);
441 	}
442 
443 	ASSERT(statep->aioms_instance == instance);
444 
445 	return (ENOTTY);
446 }
447 
448 static int
449 amd_iommu_quiesce(dev_info_t *dip)
450 {
451 	int instance = ddi_get_instance(dip);
452 	struct amd_iommu_state *statep;
453 	const char *f = "amd_iommu_quiesce";
454 
455 	statep = ddi_get_soft_state(amd_iommu_statep, instance);
456 	if (statep == NULL) {
457 		cmn_err(CE_WARN, "%s: cannot get soft state: instance %d",
458 		    f, instance);
459 		return (DDI_FAILURE);
460 	}
461 
462 	if (amd_iommu_teardown(dip, statep, AMD_IOMMU_QUIESCE) != DDI_SUCCESS) {
463 		cmn_err(CE_WARN, "%s: Unable to quiesce AMD IOMMU "
464 		    "%s%d", f, ddi_driver_name(dip), instance);
465 		return (DDI_FAILURE);
466 	}
467 
468 	return (DDI_SUCCESS);
469 }
470