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