Print this page
patch tsoome-feedback
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/common/io/devfm.c
+++ new/usr/src/uts/common/io/devfm.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 24 */
25 25
26 26 #include <sys/stat.h>
27 27 #include <sys/types.h>
28 28 #include <sys/param.h>
29 29 #include <sys/cred.h>
30 30 #include <sys/policy.h>
31 31 #include <sys/file.h>
32 32 #include <sys/errno.h>
33 33 #include <sys/modctl.h>
34 34 #include <sys/ddi.h>
35 35 #include <sys/sunddi.h>
36 36 #include <sys/conf.h>
37 37 #include <sys/debug.h>
38 38 #include <sys/systeminfo.h>
39 39
40 40 #include <sys/fm/protocol.h>
41 41 #include <sys/devfm.h>
42 42
43 43 extern int fm_get_paddr(nvlist_t *, uint64_t *);
44 44 #if defined(__x86)
45 45 extern int fm_ioctl_physcpu_info(int, nvlist_t *, nvlist_t **);
46 46 extern int fm_ioctl_cpu_retire(int, nvlist_t *, nvlist_t **);
47 47 extern int fm_ioctl_gentopo_legacy(int, nvlist_t *, nvlist_t **);
48 48 #endif /* __x86 */
49 49
50 50 static int fm_ioctl_versions(int, nvlist_t *, nvlist_t **);
51 51 static int fm_ioctl_page_retire(int, nvlist_t *, nvlist_t **);
52 52
53 53 /*
54 54 * The driver's capabilities are strictly versioned, allowing userland patching
55 55 * without a reboot. The userland should start with a FM_VERSIONS ioctl to
56 56 * query the versions of the kernel interfaces, then it's all userland's
57 57 * responsibility to prepare arguments etc to match the current kenrel.
58 58 * The version of FM_VERSIONS itself is FM_DRV_VERSION.
59 59 */
60 60 typedef struct fm_version {
61 61 char *interface; /* interface name */
62 62 uint32_t version; /* interface version */
63 63 } fm_vers_t;
64 64
65 65 typedef struct fm_subroutine {
66 66 int cmd; /* ioctl cmd */
67 67 boolean_t priv; /* require privilege */
68 68 char *version; /* version name */
69 69 int (*func)(int, nvlist_t *, nvlist_t **); /* handler */
70 70 } fm_subr_t;
71 71
72 72 static const fm_vers_t fm_versions[] = {
73 73 { FM_VERSIONS_VERSION, FM_DRV_VERSION },
74 74 { FM_PAGE_OP_VERSION, 1 },
75 75 { FM_CPU_OP_VERSION, 1 },
76 76 { FM_CPU_INFO_VERSION, 1 },
77 77 { FM_TOPO_LEGACY_VERSION, 1 },
78 78 { NULL, 0 }
79 79 };
80 80
81 81 static const fm_subr_t fm_subrs[] = {
82 82 { FM_IOC_VERSIONS, B_FALSE, FM_VERSIONS_VERSION, fm_ioctl_versions },
83 83 { FM_IOC_PAGE_RETIRE, B_TRUE, FM_PAGE_OP_VERSION,
84 84 fm_ioctl_page_retire },
85 85 { FM_IOC_PAGE_STATUS, B_FALSE, FM_PAGE_OP_VERSION,
86 86 fm_ioctl_page_retire },
87 87 { FM_IOC_PAGE_UNRETIRE, B_TRUE, FM_PAGE_OP_VERSION,
88 88 fm_ioctl_page_retire },
89 89 #if defined(__x86)
90 90 { FM_IOC_PHYSCPU_INFO, B_FALSE, FM_CPU_INFO_VERSION,
91 91 fm_ioctl_physcpu_info },
92 92 { FM_IOC_CPU_RETIRE, B_TRUE, FM_CPU_OP_VERSION,
93 93 fm_ioctl_cpu_retire },
94 94 { FM_IOC_CPU_STATUS, B_FALSE, FM_CPU_OP_VERSION,
95 95 fm_ioctl_cpu_retire },
96 96 { FM_IOC_CPU_UNRETIRE, B_TRUE, FM_CPU_OP_VERSION,
97 97 fm_ioctl_cpu_retire },
98 98 { FM_IOC_GENTOPO_LEGACY, B_FALSE, FM_TOPO_LEGACY_VERSION,
99 99 fm_ioctl_gentopo_legacy },
100 100 #endif /* __x86 */
101 101 { -1, B_FALSE, NULL, NULL },
102 102 };
103 103
104 104 static dev_info_t *fm_dip;
105 105 static boolean_t is_i86xpv;
106 106 static nvlist_t *fm_vers_nvl;
107 107
108 108 static int
109 109 fm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
110 110 {
111 111 switch (cmd) {
112 112 case DDI_ATTACH:
113 113 if (ddi_create_minor_node(dip, ddi_get_name(dip), S_IFCHR,
114 114 ddi_get_instance(dip), DDI_PSEUDO, 0) != DDI_SUCCESS) {
115 115 ddi_remove_minor_node(dip, NULL);
116 116 return (DDI_FAILURE);
117 117 }
118 118 fm_dip = dip;
119 119 is_i86xpv = (strcmp(platform, "i86xpv") == 0);
120 120 break;
121 121 case DDI_RESUME:
122 122 break;
123 123 default:
124 124 return (DDI_FAILURE);
125 125 }
126 126 return (DDI_SUCCESS);
127 127 }
128 128
129 129 static int
130 130 fm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
131 131 {
132 132 int ret = DDI_SUCCESS;
133 133
134 134 switch (cmd) {
135 135 case DDI_DETACH:
136 136 ddi_remove_minor_node(dip, NULL);
137 137 fm_dip = NULL;
138 138 break;
139 139 default:
140 140 ret = DDI_FAILURE;
141 141 }
142 142 return (ret);
143 143 }
144 144
145 145 /*ARGSUSED*/
146 146 static int
147 147 fm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
148 148 {
149 149 int error;
150 150
151 151 switch (infocmd) {
152 152 case DDI_INFO_DEVT2DEVINFO:
153 153 *result = fm_dip;
154 154 error = DDI_SUCCESS;
155 155 break;
156 156 case DDI_INFO_DEVT2INSTANCE:
157 157 *result = NULL;
158 158 error = DDI_SUCCESS;
159 159 break;
160 160 default:
161 161 error = DDI_FAILURE;
162 162 }
163 163 return (error);
164 164 }
165 165
166 166 /*ARGSUSED1*/
167 167 static int
168 168 fm_open(dev_t *devp, int flag, int typ, struct cred *cred)
169 169 {
170 170 if (typ != OTYP_CHR)
171 171 return (EINVAL);
172 172 if (getminor(*devp) != 0)
173 173 return (ENXIO);
174 174
175 175 return (0);
176 176 }
177 177
178 178 /*ARGSUSED*/
179 179 static int
180 180 fm_ioctl_versions(int cmd, nvlist_t *invl, nvlist_t **onvlp)
181 181 {
182 182 nvlist_t *nvl;
183 183 int err;
184 184
185 185 if ((err = nvlist_dup(fm_vers_nvl, &nvl, KM_SLEEP)) == 0)
186 186 *onvlp = nvl;
187 187
188 188 return (err);
189 189 }
190 190
191 191 /*
192 192 * Given a mem-scheme FMRI for a page, execute the given page retire
193 193 * command on it.
194 194 */
195 195 /*ARGSUSED*/
196 196 static int
197 197 fm_ioctl_page_retire(int cmd, nvlist_t *invl, nvlist_t **onvlp)
198 198 {
199 199 uint64_t pa;
200 200 nvlist_t *fmri;
201 201 int err;
202 202
203 203 if (is_i86xpv)
204 204 return (ENOTSUP);
205 205
206 206 if ((err = nvlist_lookup_nvlist(invl, FM_PAGE_RETIRE_FMRI, &fmri))
207 207 != 0)
208 208 return (err);
209 209
210 210 if ((err = fm_get_paddr(fmri, &pa)) != 0)
211 211 return (err);
212 212
213 213 switch (cmd) {
214 214 case FM_IOC_PAGE_STATUS:
215 215 return (page_retire_check(pa, NULL));
216 216
217 217 case FM_IOC_PAGE_RETIRE:
218 218 return (page_retire(pa, PR_FMA));
219 219
220 220 case FM_IOC_PAGE_UNRETIRE:
221 221 return (page_unretire(pa));
222 222 }
223 223
224 224 return (ENOTTY);
225 225 }
226 226
227 227 /*ARGSUSED*/
228 228 static int
229 229 fm_ioctl(dev_t dev, int cmd, intptr_t data, int flag, cred_t *cred, int *rvalp)
230 230 {
231 231 char *buf;
232 232 int err;
233 233 uint_t model;
234 234 const fm_subr_t *subr;
235 235 uint32_t vers;
236 236 fm_ioc_data_t fid;
237 237 nvlist_t *invl = NULL, *onvl = NULL;
238 238 #ifdef _MULTI_DATAMODEL
239 239 fm_ioc_data32_t fid32;
240 240 #endif
241 241
242 242 if (getminor(dev) != 0)
243 243 return (ENXIO);
244 244
245 245 for (subr = fm_subrs; subr->cmd != cmd; subr++)
246 246 if (subr->cmd == -1)
247 247 return (ENOTTY);
248 248
249 249 if (subr->priv && (flag & FWRITE) == 0 &&
250 250 secpolicy_sys_config(CRED(), 0) != 0)
251 251 return (EPERM);
252 252
253 253 model = ddi_model_convert_from(flag & FMODELS);
254 254
255 255 switch (model) {
256 256 #ifdef _MULTI_DATAMODEL
257 257 case DDI_MODEL_ILP32:
258 258 if (ddi_copyin((void *)data, &fid32,
259 259 sizeof (fm_ioc_data32_t), flag) != 0)
260 260 return (EFAULT);
261 261 fid.fid_version = fid32.fid_version;
262 262 fid.fid_insz = fid32.fid_insz;
263 263 fid.fid_inbuf = (caddr_t)(uintptr_t)fid32.fid_inbuf;
264 264 fid.fid_outsz = fid32.fid_outsz;
265 265 fid.fid_outbuf = (caddr_t)(uintptr_t)fid32.fid_outbuf;
266 266 break;
267 267 #endif /* _MULTI_DATAMODEL */
268 268 case DDI_MODEL_NONE:
269 269 default:
270 270 if (ddi_copyin((void *)data, &fid, sizeof (fm_ioc_data_t),
271 271 flag) != 0)
272 272 return (EFAULT);
273 273 }
274 274
275 275 if (nvlist_lookup_uint32(fm_vers_nvl, subr->version, &vers) != 0 ||
276 276 fid.fid_version != vers)
277 277 return (ENOTSUP);
278 278
279 279 if (fid.fid_insz > FM_IOC_MAXBUFSZ)
280 280 return (ENAMETOOLONG);
281 281 if (fid.fid_outsz > FM_IOC_OUT_MAXBUFSZ)
282 282 return (EINVAL);
283 283
284 284 /*
285 285 * Copy in and unpack the input nvlist.
286 286 */
287 287 if (fid.fid_insz != 0 && fid.fid_inbuf != (caddr_t)0) {
288 288 buf = kmem_alloc(fid.fid_insz, KM_SLEEP);
289 289 if (ddi_copyin(fid.fid_inbuf, buf, fid.fid_insz, flag) != 0) {
290 290 kmem_free(buf, fid.fid_insz);
↓ open down ↓ |
290 lines elided |
↑ open up ↑ |
291 291 return (EFAULT);
292 292 }
293 293 err = nvlist_unpack(buf, fid.fid_insz, &invl, KM_SLEEP);
294 294 kmem_free(buf, fid.fid_insz);
295 295 if (err != 0)
296 296 return (err);
297 297 }
298 298
299 299 err = subr->func(cmd, invl, &onvl);
300 300
301 - if (invl != NULL)
302 - nvlist_free(invl);
301 + nvlist_free(invl);
303 302
304 303 if (err != 0) {
305 - if (onvl != NULL)
306 - nvlist_free(onvl);
304 + nvlist_free(onvl);
307 305 return (err);
308 306 }
309 307
310 308 /*
311 309 * If the output nvlist contains any data, pack it and copyout.
312 310 */
313 311 if (onvl != NULL) {
314 312 size_t sz;
315 313
316 314 if ((err = nvlist_size(onvl, &sz, NV_ENCODE_NATIVE)) != 0) {
317 315 nvlist_free(onvl);
318 316 return (err);
319 317 }
320 318 if (sz > fid.fid_outsz) {
321 319 nvlist_free(onvl);
322 320 return (ENAMETOOLONG);
323 321 }
324 322
325 323 buf = kmem_alloc(sz, KM_SLEEP);
326 324 if ((err = nvlist_pack(onvl, &buf, &sz, NV_ENCODE_NATIVE,
327 325 KM_SLEEP)) != 0) {
328 326 kmem_free(buf, sz);
329 327 nvlist_free(onvl);
330 328 return (err);
331 329 }
332 330 nvlist_free(onvl);
333 331 if (ddi_copyout(buf, fid.fid_outbuf, sz, flag) != 0) {
334 332 kmem_free(buf, sz);
335 333 return (EFAULT);
336 334 }
337 335 kmem_free(buf, sz);
338 336 fid.fid_outsz = sz;
339 337
340 338 switch (model) {
341 339 #ifdef _MULTI_DATAMODEL
342 340 case DDI_MODEL_ILP32:
343 341 fid32.fid_outsz = (size32_t)fid.fid_outsz;
344 342 if (ddi_copyout(&fid32, (void *)data,
345 343 sizeof (fm_ioc_data32_t), flag) != 0)
346 344 return (EFAULT);
347 345 break;
348 346 #endif /* _MULTI_DATAMODEL */
349 347 case DDI_MODEL_NONE:
350 348 default:
351 349 if (ddi_copyout(&fid, (void *)data,
352 350 sizeof (fm_ioc_data_t), flag) != 0)
353 351 return (EFAULT);
354 352 }
355 353 }
356 354
357 355 return (err);
358 356 }
359 357
360 358 static struct cb_ops fm_cb_ops = {
361 359 fm_open, /* open */
362 360 nulldev, /* close */
363 361 nodev, /* strategy */
364 362 nodev, /* print */
365 363 nodev, /* dump */
366 364 nodev, /* read */
367 365 nodev, /* write */
368 366 fm_ioctl, /* ioctl */
369 367 nodev, /* devmap */
370 368 nodev, /* mmap */
371 369 nodev, /* segmap */
372 370 nochpoll, /* poll */
373 371 ddi_prop_op, /* prop_op */
374 372 NULL, /* streamtab */
375 373 D_NEW | D_MP | D_64BIT | D_U64BIT
376 374 };
377 375
378 376 static struct dev_ops fm_ops = {
379 377 DEVO_REV, /* devo_rev, */
380 378 0, /* refcnt */
381 379 fm_info, /* get_dev_info */
382 380 nulldev, /* identify */
383 381 nulldev, /* probe */
384 382 fm_attach, /* attach */
385 383 fm_detach, /* detach */
386 384 nodev, /* reset */
387 385 &fm_cb_ops, /* driver operations */
388 386 (struct bus_ops *)0 /* bus operations */
389 387 };
390 388
391 389 static struct modldrv modldrv = {
392 390 &mod_driverops, "fault management driver", &fm_ops,
393 391 };
394 392
395 393 static struct modlinkage modlinkage = {
396 394 MODREV_1, &modldrv, NULL
397 395 };
398 396
399 397 int
400 398 _init(void)
401 399 {
402 400 const fm_vers_t *p;
403 401 int ret;
404 402
405 403
406 404 if ((ret = mod_install(&modlinkage)) == 0) {
407 405 (void) nvlist_alloc(&fm_vers_nvl, NV_UNIQUE_NAME, KM_SLEEP);
408 406 for (p = fm_versions; p->interface != NULL; p++)
409 407 (void) nvlist_add_uint32(fm_vers_nvl, p->interface,
410 408 p->version);
411 409 }
412 410
413 411 return (ret);
414 412 }
415 413
416 414 int
417 415 _info(struct modinfo *modinfop)
↓ open down ↓ |
101 lines elided |
↑ open up ↑ |
418 416 {
419 417 return (mod_info(&modlinkage, modinfop));
420 418 }
421 419
422 420 int
423 421 _fini(void)
424 422 {
425 423 int ret;
426 424
427 425 if ((ret = mod_remove(&modlinkage)) == 0) {
428 - if (fm_vers_nvl != NULL)
429 - nvlist_free(fm_vers_nvl);
426 + nvlist_free(fm_vers_nvl);
430 427 }
431 428
432 429 return (ret);
433 430 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX