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 * Copyright 2023 Oxide Computer Company
31 #include <sys/types.h>
32 #include <sys/param.h>
34 #include <sys/policy.h>
36 #include <sys/errno.h>
37 #include <sys/modctl.h>
39 #include <sys/sunddi.h>
41 #include <sys/debug.h>
42 #include <sys/systeminfo.h>
44 #include <sys/fm/protocol.h>
45 #include <sys/devfm.h>
47 extern int fm_get_paddr(nvlist_t
*, uint64_t *);
49 extern int fm_ioctl_physcpu_info(int, nvlist_t
*, nvlist_t
**);
50 extern int fm_ioctl_cpu_retire(int, nvlist_t
*, nvlist_t
**);
51 extern int fm_ioctl_gentopo_legacy(int, nvlist_t
*, nvlist_t
**);
53 extern int fm_ioctl_cache_info(int, nvlist_t
*, nvlist_t
**);
55 static int fm_ioctl_versions(int, nvlist_t
*, nvlist_t
**);
56 static int fm_ioctl_page_retire(int, nvlist_t
*, nvlist_t
**);
59 * The driver's capabilities are strictly versioned, allowing userland patching
60 * without a reboot. The userland should start with a FM_VERSIONS ioctl to
61 * query the versions of the kernel interfaces, then it's all userland's
62 * responsibility to prepare arguments etc to match the current kenrel.
63 * The version of FM_VERSIONS itself is FM_DRV_VERSION.
65 typedef struct fm_version
{
66 char *interface
; /* interface name */
67 uint32_t version
; /* interface version */
70 typedef struct fm_subroutine
{
71 int cmd
; /* ioctl cmd */
72 boolean_t priv
; /* require privilege */
73 char *version
; /* version name */
74 int (*func
)(int, nvlist_t
*, nvlist_t
**); /* handler */
77 static const fm_vers_t fm_versions
[] = {
78 { FM_VERSIONS_VERSION
, FM_DRV_VERSION
},
79 { FM_PAGE_OP_VERSION
, 1 },
80 { FM_CPU_OP_VERSION
, 1 },
81 { FM_CPU_INFO_VERSION
, 1 },
82 { FM_TOPO_LEGACY_VERSION
, 1 },
83 { FM_CACHE_INFO_VERSION
, 1 },
87 static const fm_subr_t fm_subrs
[] = {
88 { FM_IOC_VERSIONS
, B_FALSE
, FM_VERSIONS_VERSION
, fm_ioctl_versions
},
89 { FM_IOC_PAGE_RETIRE
, B_TRUE
, FM_PAGE_OP_VERSION
,
90 fm_ioctl_page_retire
},
91 { FM_IOC_PAGE_STATUS
, B_FALSE
, FM_PAGE_OP_VERSION
,
92 fm_ioctl_page_retire
},
93 { FM_IOC_PAGE_UNRETIRE
, B_TRUE
, FM_PAGE_OP_VERSION
,
94 fm_ioctl_page_retire
},
96 { FM_IOC_PHYSCPU_INFO
, B_FALSE
, FM_CPU_INFO_VERSION
,
97 fm_ioctl_physcpu_info
},
98 { FM_IOC_CPU_RETIRE
, B_TRUE
, FM_CPU_OP_VERSION
,
99 fm_ioctl_cpu_retire
},
100 { FM_IOC_CPU_STATUS
, B_FALSE
, FM_CPU_OP_VERSION
,
101 fm_ioctl_cpu_retire
},
102 { FM_IOC_CPU_UNRETIRE
, B_TRUE
, FM_CPU_OP_VERSION
,
103 fm_ioctl_cpu_retire
},
104 { FM_IOC_GENTOPO_LEGACY
, B_FALSE
, FM_TOPO_LEGACY_VERSION
,
105 fm_ioctl_gentopo_legacy
},
107 { FM_IOC_CACHE_INFO
, B_FALSE
, FM_CACHE_INFO_VERSION
,
108 fm_ioctl_cache_info
},
109 { -1, B_FALSE
, NULL
, NULL
},
112 static dev_info_t
*fm_dip
;
113 static boolean_t is_i86xpv
;
114 static nvlist_t
*fm_vers_nvl
;
117 fm_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
121 if (ddi_create_minor_node(dip
, ddi_get_name(dip
), S_IFCHR
,
122 ddi_get_instance(dip
), DDI_PSEUDO
, 0) != DDI_SUCCESS
) {
123 ddi_remove_minor_node(dip
, NULL
);
124 return (DDI_FAILURE
);
127 is_i86xpv
= (strcmp(platform
, "i86xpv") == 0);
132 return (DDI_FAILURE
);
134 return (DDI_SUCCESS
);
138 fm_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
140 int ret
= DDI_SUCCESS
;
144 ddi_remove_minor_node(dip
, NULL
);
155 fm_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
160 case DDI_INFO_DEVT2DEVINFO
:
164 case DDI_INFO_DEVT2INSTANCE
:
176 fm_open(dev_t
*devp
, int flag
, int typ
, struct cred
*cred
)
180 if (getminor(*devp
) != 0)
188 fm_ioctl_versions(int cmd
, nvlist_t
*invl
, nvlist_t
**onvlp
)
193 if ((err
= nvlist_dup(fm_vers_nvl
, &nvl
, KM_SLEEP
)) == 0)
200 * Given a mem-scheme FMRI for a page, execute the given page retire
205 fm_ioctl_page_retire(int cmd
, nvlist_t
*invl
, nvlist_t
**onvlp
)
214 if ((err
= nvlist_lookup_nvlist(invl
, FM_PAGE_RETIRE_FMRI
, &fmri
))
218 if ((err
= fm_get_paddr(fmri
, &pa
)) != 0)
222 case FM_IOC_PAGE_STATUS
:
223 return (page_retire_check(pa
, NULL
));
225 case FM_IOC_PAGE_RETIRE
:
226 return (page_retire(pa
, PR_FMA
));
228 case FM_IOC_PAGE_UNRETIRE
:
229 return (page_unretire(pa
));
237 fm_ioctl(dev_t dev
, int cmd
, intptr_t data
, int flag
, cred_t
*cred
, int *rvalp
)
242 const fm_subr_t
*subr
;
245 nvlist_t
*invl
= NULL
, *onvl
= NULL
;
246 #ifdef _MULTI_DATAMODEL
247 fm_ioc_data32_t fid32
;
250 if (getminor(dev
) != 0)
253 for (subr
= fm_subrs
; subr
->cmd
!= cmd
; subr
++)
257 if (subr
->priv
&& (flag
& FWRITE
) == 0 &&
258 secpolicy_sys_config(CRED(), 0) != 0)
261 model
= ddi_model_convert_from(flag
& FMODELS
);
264 #ifdef _MULTI_DATAMODEL
265 case DDI_MODEL_ILP32
:
266 if (ddi_copyin((void *)data
, &fid32
,
267 sizeof (fm_ioc_data32_t
), flag
) != 0)
269 fid
.fid_version
= fid32
.fid_version
;
270 fid
.fid_insz
= fid32
.fid_insz
;
271 fid
.fid_inbuf
= (caddr_t
)(uintptr_t)fid32
.fid_inbuf
;
272 fid
.fid_outsz
= fid32
.fid_outsz
;
273 fid
.fid_outbuf
= (caddr_t
)(uintptr_t)fid32
.fid_outbuf
;
275 #endif /* _MULTI_DATAMODEL */
278 if (ddi_copyin((void *)data
, &fid
, sizeof (fm_ioc_data_t
),
283 if (nvlist_lookup_uint32(fm_vers_nvl
, subr
->version
, &vers
) != 0 ||
284 fid
.fid_version
!= vers
)
287 if (fid
.fid_insz
> FM_IOC_MAXBUFSZ
)
288 return (ENAMETOOLONG
);
289 if (fid
.fid_outsz
> FM_IOC_OUT_MAXBUFSZ
)
293 * Copy in and unpack the input nvlist.
295 if (fid
.fid_insz
!= 0 && fid
.fid_inbuf
!= (caddr_t
)0) {
296 buf
= kmem_alloc(fid
.fid_insz
, KM_SLEEP
);
297 if (ddi_copyin(fid
.fid_inbuf
, buf
, fid
.fid_insz
, flag
) != 0) {
298 kmem_free(buf
, fid
.fid_insz
);
301 err
= nvlist_unpack(buf
, fid
.fid_insz
, &invl
, KM_SLEEP
);
302 kmem_free(buf
, fid
.fid_insz
);
307 err
= subr
->func(cmd
, invl
, &onvl
);
317 * If the output nvlist contains any data, pack it and copyout.
322 if ((err
= nvlist_size(onvl
, &sz
, NV_ENCODE_NATIVE
)) != 0) {
326 if (sz
> fid
.fid_outsz
) {
328 return (ENAMETOOLONG
);
331 buf
= kmem_alloc(sz
, KM_SLEEP
);
332 if ((err
= nvlist_pack(onvl
, &buf
, &sz
, NV_ENCODE_NATIVE
,
339 if (ddi_copyout(buf
, fid
.fid_outbuf
, sz
, flag
) != 0) {
347 #ifdef _MULTI_DATAMODEL
348 case DDI_MODEL_ILP32
:
349 fid32
.fid_outsz
= (size32_t
)fid
.fid_outsz
;
350 if (ddi_copyout(&fid32
, (void *)data
,
351 sizeof (fm_ioc_data32_t
), flag
) != 0)
354 #endif /* _MULTI_DATAMODEL */
357 if (ddi_copyout(&fid
, (void *)data
,
358 sizeof (fm_ioc_data_t
), flag
) != 0)
366 static struct cb_ops fm_cb_ops
= {
369 nodev
, /* strategy */
374 fm_ioctl
, /* ioctl */
379 ddi_prop_op
, /* prop_op */
380 NULL
, /* streamtab */
381 D_NEW
| D_MP
| D_64BIT
| D_U64BIT
384 static struct dev_ops fm_ops
= {
385 DEVO_REV
, /* devo_rev, */
387 fm_info
, /* get_dev_info */
388 nulldev
, /* identify */
390 fm_attach
, /* attach */
391 fm_detach
, /* detach */
393 &fm_cb_ops
, /* driver operations */
394 (struct bus_ops
*)0 /* bus operations */
397 static struct modldrv modldrv
= {
398 &mod_driverops
, "fault management driver", &fm_ops
,
401 static struct modlinkage modlinkage
= {
402 MODREV_1
, &modldrv
, NULL
412 if ((ret
= mod_install(&modlinkage
)) == 0) {
413 (void) nvlist_alloc(&fm_vers_nvl
, NV_UNIQUE_NAME
, KM_SLEEP
);
414 for (p
= fm_versions
; p
->interface
!= NULL
; p
++)
415 (void) nvlist_add_uint32(fm_vers_nvl
, p
->interface
,
423 _info(struct modinfo
*modinfop
)
425 return (mod_info(&modlinkage
, modinfop
));
433 if ((ret
= mod_remove(&modlinkage
)) == 0) {
434 nvlist_free(fm_vers_nvl
);