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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 #include <sys/types.h>
28 #include <sys/param.h>
30 #include <sys/policy.h>
32 #include <sys/errno.h>
33 #include <sys/modctl.h>
35 #include <sys/sunddi.h>
37 #include <sys/debug.h>
38 #include <sys/systeminfo.h>
40 #include <sys/fm/protocol.h>
41 #include <sys/devfm.h>
43 extern int fm_get_paddr(nvlist_t
*, uint64_t *);
45 extern int fm_ioctl_physcpu_info(int, nvlist_t
*, nvlist_t
**);
46 extern int fm_ioctl_cpu_retire(int, nvlist_t
*, nvlist_t
**);
47 extern int fm_ioctl_gentopo_legacy(int, nvlist_t
*, nvlist_t
**);
50 static int fm_ioctl_versions(int, nvlist_t
*, nvlist_t
**);
51 static int fm_ioctl_page_retire(int, nvlist_t
*, nvlist_t
**);
54 * The driver's capabilities are strictly versioned, allowing userland patching
55 * without a reboot. The userland should start with a FM_VERSIONS ioctl to
56 * query the versions of the kernel interfaces, then it's all userland's
57 * responsibility to prepare arguments etc to match the current kenrel.
58 * The version of FM_VERSIONS itself is FM_DRV_VERSION.
60 typedef struct fm_version
{
61 char *interface
; /* interface name */
62 uint32_t version
; /* interface version */
65 typedef struct fm_subroutine
{
66 int cmd
; /* ioctl cmd */
67 boolean_t priv
; /* require privilege */
68 char *version
; /* version name */
69 int (*func
)(int, nvlist_t
*, nvlist_t
**); /* handler */
72 static const fm_vers_t fm_versions
[] = {
73 { FM_VERSIONS_VERSION
, FM_DRV_VERSION
},
74 { FM_PAGE_OP_VERSION
, 1 },
75 { FM_CPU_OP_VERSION
, 1 },
76 { FM_CPU_INFO_VERSION
, 1 },
77 { FM_TOPO_LEGACY_VERSION
, 1 },
81 static const fm_subr_t fm_subrs
[] = {
82 { FM_IOC_VERSIONS
, B_FALSE
, FM_VERSIONS_VERSION
, fm_ioctl_versions
},
83 { FM_IOC_PAGE_RETIRE
, B_TRUE
, FM_PAGE_OP_VERSION
,
84 fm_ioctl_page_retire
},
85 { FM_IOC_PAGE_STATUS
, B_FALSE
, FM_PAGE_OP_VERSION
,
86 fm_ioctl_page_retire
},
87 { FM_IOC_PAGE_UNRETIRE
, B_TRUE
, FM_PAGE_OP_VERSION
,
88 fm_ioctl_page_retire
},
90 { FM_IOC_PHYSCPU_INFO
, B_FALSE
, FM_CPU_INFO_VERSION
,
91 fm_ioctl_physcpu_info
},
92 { FM_IOC_CPU_RETIRE
, B_TRUE
, FM_CPU_OP_VERSION
,
93 fm_ioctl_cpu_retire
},
94 { FM_IOC_CPU_STATUS
, B_FALSE
, FM_CPU_OP_VERSION
,
95 fm_ioctl_cpu_retire
},
96 { FM_IOC_CPU_UNRETIRE
, B_TRUE
, FM_CPU_OP_VERSION
,
97 fm_ioctl_cpu_retire
},
98 { FM_IOC_GENTOPO_LEGACY
, B_FALSE
, FM_TOPO_LEGACY_VERSION
,
99 fm_ioctl_gentopo_legacy
},
101 { -1, B_FALSE
, NULL
, NULL
},
104 static dev_info_t
*fm_dip
;
105 static boolean_t is_i86xpv
;
106 static nvlist_t
*fm_vers_nvl
;
109 fm_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
113 if (ddi_create_minor_node(dip
, ddi_get_name(dip
), S_IFCHR
,
114 ddi_get_instance(dip
), DDI_PSEUDO
, 0) != DDI_SUCCESS
) {
115 ddi_remove_minor_node(dip
, NULL
);
116 return (DDI_FAILURE
);
119 is_i86xpv
= (strcmp(platform
, "i86xpv") == 0);
124 return (DDI_FAILURE
);
126 return (DDI_SUCCESS
);
130 fm_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
132 int ret
= DDI_SUCCESS
;
136 ddi_remove_minor_node(dip
, NULL
);
147 fm_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
152 case DDI_INFO_DEVT2DEVINFO
:
156 case DDI_INFO_DEVT2INSTANCE
:
168 fm_open(dev_t
*devp
, int flag
, int typ
, struct cred
*cred
)
172 if (getminor(*devp
) != 0)
180 fm_ioctl_versions(int cmd
, nvlist_t
*invl
, nvlist_t
**onvlp
)
185 if ((err
= nvlist_dup(fm_vers_nvl
, &nvl
, KM_SLEEP
)) == 0)
192 * Given a mem-scheme FMRI for a page, execute the given page retire
197 fm_ioctl_page_retire(int cmd
, nvlist_t
*invl
, nvlist_t
**onvlp
)
206 if ((err
= nvlist_lookup_nvlist(invl
, FM_PAGE_RETIRE_FMRI
, &fmri
))
210 if ((err
= fm_get_paddr(fmri
, &pa
)) != 0)
214 case FM_IOC_PAGE_STATUS
:
215 return (page_retire_check(pa
, NULL
));
217 case FM_IOC_PAGE_RETIRE
:
218 return (page_retire(pa
, PR_FMA
));
220 case FM_IOC_PAGE_UNRETIRE
:
221 return (page_unretire(pa
));
229 fm_ioctl(dev_t dev
, int cmd
, intptr_t data
, int flag
, cred_t
*cred
, int *rvalp
)
234 const fm_subr_t
*subr
;
237 nvlist_t
*invl
= NULL
, *onvl
= NULL
;
238 #ifdef _MULTI_DATAMODEL
239 fm_ioc_data32_t fid32
;
242 if (getminor(dev
) != 0)
245 for (subr
= fm_subrs
; subr
->cmd
!= cmd
; subr
++)
249 if (subr
->priv
&& (flag
& FWRITE
) == 0 &&
250 secpolicy_sys_config(CRED(), 0) != 0)
253 model
= ddi_model_convert_from(flag
& FMODELS
);
256 #ifdef _MULTI_DATAMODEL
257 case DDI_MODEL_ILP32
:
258 if (ddi_copyin((void *)data
, &fid32
,
259 sizeof (fm_ioc_data32_t
), flag
) != 0)
261 fid
.fid_version
= fid32
.fid_version
;
262 fid
.fid_insz
= fid32
.fid_insz
;
263 fid
.fid_inbuf
= (caddr_t
)(uintptr_t)fid32
.fid_inbuf
;
264 fid
.fid_outsz
= fid32
.fid_outsz
;
265 fid
.fid_outbuf
= (caddr_t
)(uintptr_t)fid32
.fid_outbuf
;
267 #endif /* _MULTI_DATAMODEL */
270 if (ddi_copyin((void *)data
, &fid
, sizeof (fm_ioc_data_t
),
275 if (nvlist_lookup_uint32(fm_vers_nvl
, subr
->version
, &vers
) != 0 ||
276 fid
.fid_version
!= vers
)
279 if (fid
.fid_insz
> FM_IOC_MAXBUFSZ
)
280 return (ENAMETOOLONG
);
281 if (fid
.fid_outsz
> FM_IOC_OUT_MAXBUFSZ
)
285 * Copy in and unpack the input nvlist.
287 if (fid
.fid_insz
!= 0 && fid
.fid_inbuf
!= (caddr_t
)0) {
288 buf
= kmem_alloc(fid
.fid_insz
, KM_SLEEP
);
289 if (ddi_copyin(fid
.fid_inbuf
, buf
, fid
.fid_insz
, flag
) != 0) {
290 kmem_free(buf
, fid
.fid_insz
);
293 err
= nvlist_unpack(buf
, fid
.fid_insz
, &invl
, KM_SLEEP
);
294 kmem_free(buf
, fid
.fid_insz
);
299 err
= subr
->func(cmd
, invl
, &onvl
);
309 * If the output nvlist contains any data, pack it and copyout.
314 if ((err
= nvlist_size(onvl
, &sz
, NV_ENCODE_NATIVE
)) != 0) {
318 if (sz
> fid
.fid_outsz
) {
320 return (ENAMETOOLONG
);
323 buf
= kmem_alloc(sz
, KM_SLEEP
);
324 if ((err
= nvlist_pack(onvl
, &buf
, &sz
, NV_ENCODE_NATIVE
,
331 if (ddi_copyout(buf
, fid
.fid_outbuf
, sz
, flag
) != 0) {
339 #ifdef _MULTI_DATAMODEL
340 case DDI_MODEL_ILP32
:
341 fid32
.fid_outsz
= (size32_t
)fid
.fid_outsz
;
342 if (ddi_copyout(&fid32
, (void *)data
,
343 sizeof (fm_ioc_data32_t
), flag
) != 0)
346 #endif /* _MULTI_DATAMODEL */
349 if (ddi_copyout(&fid
, (void *)data
,
350 sizeof (fm_ioc_data_t
), flag
) != 0)
358 static struct cb_ops fm_cb_ops
= {
361 nodev
, /* strategy */
366 fm_ioctl
, /* ioctl */
371 ddi_prop_op
, /* prop_op */
372 NULL
, /* streamtab */
373 D_NEW
| D_MP
| D_64BIT
| D_U64BIT
376 static struct dev_ops fm_ops
= {
377 DEVO_REV
, /* devo_rev, */
379 fm_info
, /* get_dev_info */
380 nulldev
, /* identify */
382 fm_attach
, /* attach */
383 fm_detach
, /* detach */
385 &fm_cb_ops
, /* driver operations */
386 NULL
/* bus operations */
389 static struct modldrv modldrv
= {
390 &mod_driverops
, "fault management driver", &fm_ops
,
393 static struct modlinkage modlinkage
= {
394 MODREV_1
, &modldrv
, NULL
404 if ((ret
= mod_install(&modlinkage
)) == 0) {
405 (void) nvlist_alloc(&fm_vers_nvl
, NV_UNIQUE_NAME
, KM_SLEEP
);
406 for (p
= fm_versions
; p
->interface
!= NULL
; p
++)
407 (void) nvlist_add_uint32(fm_vers_nvl
, p
->interface
,
415 _info(struct modinfo
*modinfop
)
417 return (mod_info(&modlinkage
, modinfop
));
425 if ((ret
= mod_remove(&modlinkage
)) == 0) {
426 nvlist_free(fm_vers_nvl
);