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
_init(void)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
_info(struct modinfo * modinfop)187 _info(struct modinfo *modinfop)
188 {
189 return (mod_info(&modlinkage, modinfop));
190 }
191
192 int
_fini(void)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
amd_iommu_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)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
amd_iommu_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
amd_iommu_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
amd_iommu_open(dev_t * devp,int flag,int otyp,cred_t * credp)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
amd_iommu_close(dev_t dev,int flag,int otyp,cred_t * credp)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
amd_iommu_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)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
amd_iommu_quiesce(dev_info_t * dip)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