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 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include <sys/types.h>
30 #include <sys/systm.h>
31 #include <sys/ddi.h>
32 #include <sys/sunddi.h>
33 #include <sys/ddi_impldefs.h>
34 #include <sys/obpdefs.h>
35 #include <sys/errno.h>
36 #include <sys/kmem.h>
37 #include <sys/vmem.h>
38 #include <sys/debug.h>
39 #include <sys/sysmacros.h>
40 #include <sys/machsystm.h>
41 #include <sys/machparam.h>
42 #include <sys/modctl.h>
43 #include <sys/fhc.h>
44 #include <sys/ac.h>
45 #include <sys/vm.h>
46 #include <sys/cpu_module.h>
47 #include <vm/seg_kmem.h>
48 #include <vm/hat_sfmmu.h>
49 #include <sys/mem_config.h>
50 #include <sys/mem_cage.h>
51
52 extern ac_err_t ac_kpm_err_cvt(int);
53
54 int ac_del_clean = 0;
55
56 /*
57 * Default timeout, in seconds, for delete.
58 * Time is counted when no progress is being made.
59 */
60 static int ac_del_timeout = 60;
61
62 #define DEL_PAGESIZE MMU_PAGESIZE
63
64 struct del_status {
65 struct del_status *next;
66 memhandle_t handle;
67 volatile int its_done;
68 int done_error;
69 kcondvar_t ac_del_cv;
70 int del_timeout;
71 int del_noprogress;
72 ac_err_t cancel_code;
73 timeout_id_t to_id;
74 pgcnt_t last_collected;
75 };
76 static struct del_status *ac_del_list;
77 static kmutex_t ac_del_mutex;
78
79 static struct del_status *
ac_del_alloc_status()80 ac_del_alloc_status()
81 {
82 struct del_status *dsp;
83
84 dsp = (struct del_status *)kmem_zalloc(sizeof (*dsp), KM_SLEEP);
85 mutex_enter(&ac_del_mutex);
86 dsp->next = ac_del_list;
87 ac_del_list = dsp;
88 mutex_exit(&ac_del_mutex);
89
90 return (dsp);
91 }
92
93 static void
ac_del_free_status(struct del_status * dsp)94 ac_del_free_status(struct del_status *dsp)
95 {
96 struct del_status **dspp;
97
98 mutex_enter(&ac_del_mutex);
99 dspp = &ac_del_list;
100 while (*dspp != NULL) {
101 if (*dspp == dsp)
102 break;
103 dspp = &(*dspp)->next;
104 }
105 ASSERT(*dspp == dsp);
106 if (*dspp == dsp) {
107 *dspp = dsp->next;
108 }
109 mutex_exit(&ac_del_mutex);
110 kmem_free((void *)dsp, sizeof (*dsp));
111 }
112
113 static void
del_comp(void * arg,int error)114 del_comp(void *arg, int error)
115 {
116 struct del_status *dsp;
117
118 dsp = (struct del_status *)arg;
119 mutex_enter(&ac_del_mutex);
120 #ifdef DEBUG
121 {
122 struct del_status *adsp;
123 for (adsp = ac_del_list; adsp != NULL; adsp = adsp->next) {
124 if (adsp == dsp)
125 break;
126 }
127 ASSERT(adsp != NULL);
128 }
129 #endif /* DEBUG */
130 dsp->its_done = 1;
131 dsp->done_error = error;
132 cv_signal(&dsp->ac_del_cv);
133 mutex_exit(&ac_del_mutex);
134 }
135
136 /*ARGSUSED*/
137 static void
del_to_scan(void * arg)138 del_to_scan(void *arg)
139 {
140 struct del_status *dsp;
141 int do_cancel;
142 memdelstat_t dstat;
143 int err;
144
145 dsp = arg;
146
147 #ifdef DEBUG
148 {
149 struct del_status *adsp;
150
151 mutex_enter(&ac_del_mutex);
152 for (adsp = ac_del_list; adsp != NULL; adsp = adsp->next) {
153 if (adsp == dsp)
154 break;
155 }
156 ASSERT(adsp != NULL);
157 mutex_exit(&ac_del_mutex);
158 }
159 #endif /* DEBUG */
160 do_cancel = 0;
161 err = kphysm_del_status(dsp->handle, &dstat);
162 mutex_enter(&ac_del_mutex);
163 if (dsp->its_done) {
164 mutex_exit(&ac_del_mutex);
165 return;
166 }
167 if ((err == KPHYSM_OK) &&
168 (dsp->last_collected != dstat.collected)) {
169 dsp->del_noprogress = 0;
170 dsp->last_collected = dstat.collected;
171 } else {
172 dsp->del_noprogress++;
173 if (dsp->del_noprogress >= dsp->del_timeout) {
174 if (dsp->cancel_code == 0)
175 dsp->cancel_code = AC_ERR_TIMEOUT;
176 do_cancel = 1;
177 }
178 }
179 if (!do_cancel)
180 dsp->to_id = timeout(del_to_scan, arg, hz);
181 else
182 dsp->to_id = 0;
183 mutex_exit(&ac_del_mutex);
184 if (do_cancel)
185 (void) kphysm_del_cancel(dsp->handle);
186 }
187
188 static void
del_to_start(struct del_status * dsp)189 del_to_start(struct del_status *dsp)
190 {
191 if (dsp->del_timeout != 0)
192 dsp->to_id = timeout(del_to_scan, dsp, hz);
193 }
194
195 static void
del_to_stop(struct del_status * dsp)196 del_to_stop(struct del_status *dsp)
197 {
198 timeout_id_t tid;
199
200 while ((tid = dsp->to_id) != 0) {
201 dsp->to_id = 0;
202 mutex_exit(&ac_del_mutex);
203 (void) untimeout(tid);
204 mutex_enter(&ac_del_mutex);
205 }
206 }
207
208 static int
ac_del_bank_add_span(memhandle_t handle,ac_cfga_pkt_t * pkt)209 ac_del_bank_add_span(
210 memhandle_t handle,
211 ac_cfga_pkt_t *pkt)
212 {
213 uint64_t decode;
214 uint64_t base_pa;
215 uint64_t bank_size;
216 pfn_t base;
217 pgcnt_t npgs;
218 int errs;
219 int ret;
220 struct ac_soft_state *asp = pkt->softsp;
221 uint_t ilv;
222
223 /*
224 * Cannot delete interleaved banks at the moment.
225 */
226 ilv = (pkt->bank == Bank0) ?
227 INTLV0(*asp->ac_memctl) : INTLV1(*asp->ac_memctl);
228 if (ilv != 1) {
229 AC_ERR_SET(pkt, AC_ERR_MEM_DEINTLV);
230 return (EINVAL);
231 }
232 /*
233 * Determine the physical location of the selected bank
234 */
235 decode = (pkt->bank == Bank0) ?
236 *asp->ac_memdecode0 : *asp->ac_memdecode1;
237 base_pa = GRP_REALBASE(decode);
238 bank_size = GRP_UK2SPAN(decode);
239
240 base = base_pa >> PAGESHIFT;
241 npgs = bank_size >> PAGESHIFT;
242
243 /*
244 * Delete the pages from the cage growth list.
245 */
246 ret = kcage_range_delete(base, npgs);
247 if (ret != 0) {
248 /* TODO: Should this be a separate error? */
249 AC_ERR_SET(pkt, AC_ERR_KPM_NONRELOC);
250 return (EINVAL);
251 }
252
253 /*
254 * Add to delete memory list.
255 */
256
257 if ((errs = kphysm_del_span(handle, base, npgs)) != KPHYSM_OK) {
258 AC_ERR_SET(pkt, ac_kpm_err_cvt(errs));
259 /*
260 * Restore the pages to the cage growth list.
261 * TODO: We should not unconditionally add back
262 * if we conditionally add at memory add time.
263 */
264 errs = kcage_range_add(base, npgs, KCAGE_DOWN);
265 /* TODO: deal with error return. */
266 if (errs != 0) {
267 AC_ERR_SET(pkt, ac_kpm_err_cvt(errs));
268 cmn_err(CE_NOTE, "ac_del_bank_add_span(): "
269 "board %d, bank %d, "
270 "kcage_range_add() returned %d",
271 pkt->softsp->board, pkt->bank, errs);
272 }
273 return (EINVAL);
274 }
275 return (0);
276 }
277
278 static void
ac_del_bank_add_cage(struct bd_list * del,enum ac_bank_id bank)279 ac_del_bank_add_cage(
280 struct bd_list *del,
281 enum ac_bank_id bank)
282 {
283 uint64_t decode;
284 uint64_t base_pa;
285 uint64_t bank_size;
286 pfn_t base;
287 pgcnt_t npgs;
288 int errs;
289 struct ac_soft_state *asp = (struct ac_soft_state *)(del->ac_softsp);
290
291 /*
292 * Determine the physical location of the selected bank
293 */
294 decode = (bank == Bank0) ? *asp->ac_memdecode0 : *asp->ac_memdecode1;
295 base_pa = GRP_REALBASE(decode);
296 bank_size = GRP_UK2SPAN(decode);
297
298 base = base_pa >> PAGESHIFT;
299 npgs = bank_size >> PAGESHIFT;
300
301 /*
302 * Restore the pages to the cage growth list.
303 * TODO: We should not unconditionally add back
304 * if we conditionally add at memory add time.
305 */
306 errs = kcage_range_add(base, npgs, KCAGE_DOWN);
307 /* TODO: deal with error return. */
308 if (errs != 0)
309 cmn_err(CE_NOTE, "ac_del_bank_add_cage(): "
310 "board %d, bank %d, "
311 "kcage_range_add() returned %d",
312 del->sc.board, bank, errs);
313 }
314
315 static int
ac_del_bank_run(struct del_status * dsp,ac_cfga_pkt_t * pkt)316 ac_del_bank_run(struct del_status *dsp, ac_cfga_pkt_t *pkt)
317 {
318 int errs;
319
320 dsp->its_done = 0;
321 if ((errs = kphysm_del_start(dsp->handle, del_comp, (void *)dsp)) !=
322 KPHYSM_OK) {
323 AC_ERR_SET(pkt, ac_kpm_err_cvt(errs));
324 return (EINVAL);
325 }
326 /* Wait for it to complete. */
327 mutex_enter(&ac_del_mutex);
328 del_to_start(dsp);
329 while (!dsp->its_done) {
330 if (!cv_wait_sig(&dsp->ac_del_cv, &ac_del_mutex)) {
331 if (dsp->cancel_code == 0)
332 dsp->cancel_code = AC_ERR_INTR;
333 mutex_exit(&ac_del_mutex);
334 errs = kphysm_del_cancel(dsp->handle);
335 mutex_enter(&ac_del_mutex);
336 if (errs != KPHYSM_OK) {
337 ASSERT(errs == KPHYSM_ENOTRUNNING);
338 }
339 break;
340 }
341 }
342 /*
343 * If the loop exited due to a signal, we must continue to wait
344 * using cv_wait() as the signal is pending until syscall exit.
345 */
346 while (!dsp->its_done) {
347 cv_wait(&dsp->ac_del_cv, &ac_del_mutex);
348 }
349 if (dsp->done_error != KPHYSM_OK) {
350 AC_ERR_SET(pkt, ac_kpm_err_cvt(dsp->done_error));
351 if ((dsp->done_error == KPHYSM_ECANCELLED) ||
352 (dsp->done_error == KPHYSM_EREFUSED)) {
353 errs = EINTR;
354 if (dsp->cancel_code != 0) {
355 AC_ERR_SET(pkt, dsp->cancel_code);
356 }
357 } else {
358 errs = EINVAL;
359 }
360 } else
361 errs = 0;
362 del_to_stop(dsp);
363 mutex_exit(&ac_del_mutex);
364
365 return (errs);
366 }
367
368
369 /*
370 * set the memory to known state for debugging
371 */
372 static void
ac_bank_write_pattern(struct bd_list * del,enum ac_bank_id bank)373 ac_bank_write_pattern(struct bd_list *del, enum ac_bank_id bank)
374 {
375 uint64_t decode;
376 uint64_t base_pa;
377 uint64_t limit_pa;
378 uint64_t bank_size;
379 uint64_t current_pa;
380 caddr_t base_va;
381 caddr_t fill_buf;
382 struct ac_soft_state *asp = (struct ac_soft_state *)(del->ac_softsp);
383 int linesize;
384
385 /*
386 * Determine the physical location of the selected bank
387 */
388 decode = (bank == Bank0) ? *asp->ac_memdecode0 : *asp->ac_memdecode1;
389 base_pa = GRP_REALBASE(decode);
390 bank_size = GRP_UK2SPAN(decode);
391 limit_pa = base_pa + bank_size;
392 linesize = cpunodes[CPU->cpu_id].ecache_linesize;
393
394 /*
395 * We need a page_va and a fill buffer for this operation
396 */
397 base_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
398 fill_buf = kmem_zalloc(DEL_PAGESIZE, KM_SLEEP);
399 {
400 typedef uint32_t patt_t;
401 patt_t *bf, *bfe, patt;
402
403 bf = (patt_t *)fill_buf;
404 bfe = (patt_t *)((char *)fill_buf + DEL_PAGESIZE);
405 patt = 0xbeaddeed;
406 while (bf < bfe)
407 *bf++ = patt;
408 }
409
410 /*
411 * 'empty' the memory
412 */
413 kpreempt_disable();
414 for (current_pa = base_pa; current_pa < limit_pa;
415 current_pa += DEL_PAGESIZE) {
416
417 /* map current pa */
418 ac_mapin(current_pa, base_va);
419
420 /* fill the target page */
421 ac_blkcopy(fill_buf, base_va,
422 DEL_PAGESIZE/linesize, linesize);
423
424 /* tear down translation */
425 ac_unmap(base_va);
426 }
427 kpreempt_enable();
428
429 /*
430 * clean up temporary resources
431 */
432 {
433 /* Distinguish the fill buf from memory deleted! */
434 typedef uint32_t patt_t;
435 patt_t *bf, *bfe, patt;
436
437 bf = (patt_t *)fill_buf;
438 bfe = (patt_t *)((char *)fill_buf + DEL_PAGESIZE);
439 patt = 0xbeadfeed;
440 while (bf < bfe)
441 *bf++ = patt;
442 }
443 kmem_free(fill_buf, DEL_PAGESIZE);
444 vmem_free(heap_arena, base_va, PAGESIZE);
445 }
446
447 int
ac_del_memory(ac_cfga_pkt_t * pkt)448 ac_del_memory(ac_cfga_pkt_t *pkt)
449 {
450 struct bd_list *board;
451 struct ac_mem_info *mem_info;
452 int busy_set;
453 struct del_status *dsp;
454 memdelstat_t dstat;
455 int retval;
456 int r_errs;
457 struct ac_soft_state *asp;
458
459 if (!kcage_on) {
460 static int cage_msg_done = 0;
461
462 if (!cage_msg_done) {
463 cage_msg_done = 1;
464 cmn_err(CE_NOTE, "ac: memory delete"
465 " refused: cage is off");
466 }
467 AC_ERR_SET(pkt, ac_kpm_err_cvt(KPHYSM_ENONRELOC));
468 return (EINVAL);
469 }
470
471 dsp = ac_del_alloc_status();
472 if ((retval = kphysm_del_gethandle(&dsp->handle)) != KPHYSM_OK) {
473 ac_del_free_status(dsp);
474 AC_ERR_SET(pkt, ac_kpm_err_cvt(retval));
475 return (EINVAL);
476 }
477 retval = 0;
478 busy_set = 0;
479
480 board = fhc_bdlist_lock(pkt->softsp->board);
481 if (board == NULL || board->ac_softsp == NULL) {
482 fhc_bdlist_unlock();
483 AC_ERR_SET(pkt, AC_ERR_BD);
484 retval = EINVAL;
485 goto out;
486 }
487 ASSERT(pkt->softsp == board->ac_softsp);
488 asp = pkt->softsp;
489
490 /* verify the board is of the correct type */
491 switch (board->sc.type) {
492 case CPU_BOARD:
493 case MEM_BOARD:
494 break;
495 default:
496 fhc_bdlist_unlock();
497 AC_ERR_SET(pkt, AC_ERR_BD_TYPE);
498 retval = EINVAL;
499 goto out;
500 }
501
502 /* verify the memory condition is acceptable */
503 mem_info = &asp->bank[pkt->bank];
504 if (!MEM_BOARD_VISIBLE(board) || mem_info->busy ||
505 fhc_bd_busy(pkt->softsp->board) ||
506 mem_info->rstate != SYSC_CFGA_RSTATE_CONNECTED ||
507 mem_info->ostate != SYSC_CFGA_OSTATE_CONFIGURED) {
508 fhc_bdlist_unlock();
509 AC_ERR_SET(pkt, AC_ERR_BD_STATE);
510 retval = EINVAL;
511 goto out;
512 }
513
514 if ((dsp->del_timeout = pkt->cmd_cfga.arg) == -1)
515 dsp->del_timeout = ac_del_timeout;
516
517 /*
518 * at this point, we have an available bank to del.
519 * mark it busy and initiate the del function.
520 */
521 mem_info->busy = TRUE;
522 fhc_bdlist_unlock();
523
524 busy_set = 1;
525
526 retval = ac_del_bank_add_span(dsp->handle, pkt);
527 out:
528 if (retval != 0) {
529 r_errs = kphysm_del_release(dsp->handle);
530 ASSERT(r_errs == KPHYSM_OK);
531
532 if (busy_set) {
533 board = fhc_bdlist_lock(pkt->softsp->board);
534 ASSERT(board != NULL && board->ac_softsp != NULL);
535
536 ASSERT(board->sc.type == CPU_BOARD ||
537 board->sc.type == MEM_BOARD);
538 ASSERT(asp ==
539 (struct ac_soft_state *)(board->ac_softsp));
540 mem_info = &asp->bank[pkt->bank];
541 ASSERT(mem_info->busy != FALSE);
542 ASSERT(mem_info->ostate == SYSC_CFGA_OSTATE_CONFIGURED);
543 mem_info->busy = FALSE;
544 fhc_bdlist_unlock();
545 }
546
547 ac_del_free_status(dsp);
548 return (retval);
549 }
550
551 (void) kphysm_del_status(dsp->handle, &dstat);
552
553 retval = ac_del_bank_run(dsp, pkt);
554
555 r_errs = kphysm_del_release(dsp->handle);
556 ASSERT(r_errs == KPHYSM_OK);
557
558 board = fhc_bdlist_lock(pkt->softsp->board);
559 ASSERT(board != NULL && board->ac_softsp != NULL);
560
561 ASSERT(board->sc.type == CPU_BOARD || board->sc.type == MEM_BOARD);
562 ASSERT(asp == (struct ac_soft_state *)(board->ac_softsp));
563 mem_info = &asp->bank[pkt->bank];
564 ASSERT(mem_info->busy != FALSE);
565 ASSERT(mem_info->ostate == SYSC_CFGA_OSTATE_CONFIGURED);
566 mem_info->busy = FALSE;
567 if (retval == 0) {
568 mem_info->ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
569 mem_info->status_change = ddi_get_time();
570
571 if (ac_del_clean) {
572 /* DEBUG - set memory to known state */
573 ac_bank_write_pattern(board, pkt->bank);
574 }
575 } else {
576 /*
577 * Restore the pages to the cage growth list.
578 */
579 ac_del_bank_add_cage(board, pkt->bank);
580 }
581 fhc_bdlist_unlock();
582
583 ac_del_free_status(dsp);
584
585 return (retval);
586 }
587