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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <sys/cpr.h>
30 #include <sys/fs/ufs_fs.h>
31 #include <sys/prom_plat.h>
32 #include "cprboot.h"
33 
34 
35 /*
36  * max space for a copy of physavail data
37  * prop size is usually 80 to 128 bytes
38  */
39 #define	PA_BUFSIZE	1024
40 
41 #define	CB_SETBIT	1
42 #define	CB_ISSET	2
43 #define	CB_ISCLR	3
44 
45 /*
46  * globals
47  */
48 int cb_nbitmaps;
49 
50 /*
51  * file scope
52  */
53 static arange_t *cb_physavail;
54 static char pabuf[PA_BUFSIZE];
55 static caddr_t high_virt;
56 
57 static cbd_t cb_bmda[CPR_MAX_BMDESC];
58 static int tracking_init;
59 
60 
61 static int
cb_bitop(pfn_t ppn,int op)62 cb_bitop(pfn_t ppn, int op)
63 {
64 	int rel, rval = 0;
65 	char *bitmap;
66 	cbd_t *dp;
67 
68 	for (dp = cb_bmda; dp->cbd_size; dp++) {
69 		if (PPN_IN_RANGE(ppn, dp)) {
70 			bitmap = (char *)dp->cbd_reg_bitmap;
71 			rel = ppn - dp->cbd_spfn;
72 			if (op == CB_SETBIT)
73 				setbit(bitmap, rel);
74 			else if (op == CB_ISSET)
75 				rval = isset(bitmap, rel);
76 			else if (op == CB_ISCLR)
77 				rval = isclr(bitmap, rel);
78 			break;
79 		}
80 	}
81 
82 	return (rval);
83 }
84 
85 
86 /*
87  * count pages that are isolated from the kernel
88  * within each available range
89  */
90 static void
count_free_pages(void)91 count_free_pages(void)
92 {
93 	arange_t *arp;
94 	pfn_t bitno;
95 	int cnt;
96 
97 	for (arp = cb_physavail; arp->high; arp++) {
98 		cnt = 0;
99 		for (bitno = arp->low; bitno <= arp->high; bitno++) {
100 			if (cb_bitop(bitno, CB_ISCLR))
101 				cnt++;
102 		}
103 		arp->nfree = cnt;
104 	}
105 }
106 
107 
108 /*
109  * scan the physavail list for a page
110  * that doesn't clash with the kernel
111  */
112 static pfn_t
search_phav_pages(void)113 search_phav_pages(void)
114 {
115 	static arange_t *arp;
116 	static pfn_t bitno;
117 	int rescan;
118 
119 	if (arp == NULL) {
120 		count_free_pages();
121 		arp = cb_physavail;
122 		bitno = arp->low;
123 	}
124 
125 	/*
126 	 * begin scanning from the previous position and if the scan
127 	 * reaches the end of the list, scan a second time from the top;
128 	 * nfree is checked to eliminate scanning overhead when most
129 	 * of the available space gets used up.  when a page is found,
130 	 * set a bit so the page wont be found by another scan.
131 	 */
132 	for (rescan = 0; rescan < 2; rescan++) {
133 		for (; arp->high; bitno = (++arp)->low) {
134 			if (arp->nfree == 0)
135 				continue;
136 			for (; bitno <= arp->high; bitno++) {
137 				if (cb_bitop(bitno, CB_ISCLR)) {
138 					(void) cb_bitop(bitno, CB_SETBIT);
139 					arp->nfree--;
140 					return (bitno++);
141 				}
142 			}
143 		}
144 		arp = cb_physavail;
145 		bitno = arp->low;
146 	}
147 
148 	return (PFN_INVALID);
149 }
150 
151 
152 /*
153  * scan statefile buffer pages for reusable tmp space
154  */
155 static pfn_t
search_buf_pages(void)156 search_buf_pages(void)
157 {
158 	size_t coff, src_base;
159 	static size_t lboff;
160 	pfn_t ppn;
161 
162 	if (tracking_init == 0)
163 		return (PFN_INVALID);
164 
165 	/*
166 	 * when scanning the list of statefile buffer ppns, we know that
167 	 * all pages from lboff to the page boundary of buf_offset have
168 	 * already been restored; when the associated page bit is clear,
169 	 * that page is isolated from the kernel and we can reuse it for
170 	 * tmp space; otherwise, when SF_DIFF_PPN indicates a page had
171 	 * been moved, we know the page bit was previously clear and
172 	 * later set, and we can reuse the new page.
173 	 */
174 	src_base = sfile.buf_offset & MMU_PAGEMASK;
175 	while (lboff < src_base) {
176 		coff = lboff;
177 		lboff += MMU_PAGESIZE;
178 		ppn = SF_ORIG_PPN(coff);
179 		if (cb_bitop(ppn, CB_ISCLR)) {
180 			(void) cb_bitop(ppn, CB_SETBIT);
181 			SF_STAT_INC(recycle);
182 			return (ppn);
183 		} else if (SF_DIFF_PPN(coff)) {
184 			SF_STAT_INC(recycle);
185 			return (SF_BUF_PPN(coff));
186 		}
187 	}
188 
189 	return (PFN_INVALID);
190 }
191 
192 
193 /*
194  * scan physavail and statefile buffer page lists
195  * for a page that doesn't clash with the kernel
196  */
197 pfn_t
find_apage(void)198 find_apage(void)
199 {
200 	pfn_t ppn;
201 
202 	ppn = search_phav_pages();
203 	if (ppn != PFN_INVALID)
204 		return (ppn);
205 	ppn = search_buf_pages();
206 	if (ppn != PFN_INVALID)
207 		return (ppn);
208 
209 	prom_printf("\n%s: ran out of available/free pages!\n%s\n",
210 	    prog, rsvp);
211 	cb_exit_to_mon();
212 
213 	/* NOTREACHED */
214 	return (PFN_INVALID);
215 }
216 
217 
218 /*
219  * reserve virt range, find available phys pages,
220  * and map-in each phys starting at vaddr
221  */
222 static caddr_t
map_free_phys(caddr_t vaddr,size_t size,char * name)223 map_free_phys(caddr_t vaddr, size_t size, char *name)
224 {
225 	int pages, ppn, err;
226 	physaddr_t phys;
227 	caddr_t virt;
228 	char *str;
229 
230 	str = "map_free_phys";
231 	virt = prom_claim_virt(size, vaddr);
232 	CB_VPRINTF(("\n%s: claim vaddr 0x%p, size 0x%lx, ret 0x%p\n",
233 	    str, (void *)vaddr, size, (void *)virt));
234 	if (virt != vaddr) {
235 		prom_printf("\n%s: cant reserve (0x%p - 0x%p) for \"%s\"\n",
236 		    str, (void *)vaddr, (void *)(vaddr + size), name);
237 		return (virt);
238 	}
239 
240 	for (pages = mmu_btop(size); pages--; virt += MMU_PAGESIZE) {
241 		/*
242 		 * map virt page to free phys
243 		 */
244 		ppn = find_apage();
245 		phys = PN_TO_ADDR(ppn);
246 
247 		err = prom_map_phys(-1, MMU_PAGESIZE, virt, phys);
248 		if (err || verbose) {
249 			prom_printf("    map virt 0x%p, phys 0x%llx, "
250 			    "ppn 0x%x, ret %d\n", (void *)virt, phys, ppn, err);
251 		}
252 		if (err)
253 			return ((caddr_t)ERR);
254 	}
255 
256 	return (vaddr);
257 }
258 
259 
260 /*
261  * check bitmap desc and relocate bitmap data
262  * to pages isolated from the kernel
263  *
264  * sets globals:
265  *	high_virt
266  */
267 int
cb_set_bitmap(void)268 cb_set_bitmap(void)
269 {
270 	size_t bmda_size, all_bitmap_size, alloc_size;
271 	caddr_t newvirt, src, dst, base;
272 	cbd_t *dp;
273 	char *str;
274 
275 	str = "cb_set_bitmap";
276 	CB_VPRINTF((ent_fmt, str, entry));
277 
278 	/*
279 	 * max is checked in the cpr module;
280 	 * this condition should never occur
281 	 */
282 	if (cb_nbitmaps > (CPR_MAX_BMDESC - 1)) {
283 		prom_printf("%s: too many bitmap descriptors %d, max %d\n",
284 		    str, cb_nbitmaps, (CPR_MAX_BMDESC - 1));
285 		return (ERR);
286 	}
287 
288 	/*
289 	 * copy bitmap descriptors to aligned space, check magic numbers,
290 	 * and set the total size of all bitmaps
291 	 */
292 	bmda_size = cb_nbitmaps * sizeof (cbd_t);
293 	src = SF_DATA();
294 	bcopy(src, cb_bmda, bmda_size);
295 	base = src + bmda_size;
296 	all_bitmap_size = 0;
297 	for (dp = cb_bmda; dp < &cb_bmda[cb_nbitmaps]; dp++) {
298 		if (dp->cbd_magic != CPR_BITMAP_MAGIC) {
299 			prom_printf("%s: bad magic 0x%x, expect 0x%x\n",
300 			    str, dp->cbd_magic, CPR_BITMAP_MAGIC);
301 			return (ERR);
302 		}
303 		all_bitmap_size += dp->cbd_size;
304 		dp->cbd_reg_bitmap = (cpr_ptr)base;
305 		base += dp->cbd_size;
306 	}
307 
308 	/*
309 	 * reserve new space for bitmaps
310 	 */
311 	alloc_size = PAGE_ROUNDUP(all_bitmap_size);
312 	if (verbose || CPR_DBG(7)) {
313 		prom_printf("%s: nbitmaps %d, bmda_size 0x%lx\n",
314 		    str, cb_nbitmaps, bmda_size);
315 		prom_printf("%s: all_bitmap_size 0x%lx, alloc_size 0x%lx\n",
316 		    str, all_bitmap_size, alloc_size);
317 	}
318 	high_virt = (caddr_t)CB_HIGH_VIRT;
319 	newvirt = map_free_phys(high_virt, alloc_size, "bitmaps");
320 	if (newvirt != high_virt)
321 		return (ERR);
322 
323 	/*
324 	 * copy the bitmaps, clear any unused space trailing them,
325 	 * and set references into the new space
326 	 */
327 	base = src + bmda_size;
328 	dst = newvirt;
329 	bcopy(base, dst, all_bitmap_size);
330 	if (alloc_size > all_bitmap_size)
331 		bzero(dst + all_bitmap_size, alloc_size - all_bitmap_size);
332 	for (dp = cb_bmda; dp->cbd_size; dp++) {
333 		dp->cbd_reg_bitmap = (cpr_ptr)dst;
334 		dst += dp->cbd_size;
335 	}
336 
337 	/* advance past all the bitmap data */
338 	SF_ADV(bmda_size + all_bitmap_size);
339 	high_virt += alloc_size;
340 
341 	return (0);
342 }
343 
344 
345 /*
346  * create a new stack for cprboot;
347  * this stack is used to avoid clashes with kernel pages and
348  * to avoid exceptions while remapping cprboot virt pages
349  */
350 int
cb_get_newstack(void)351 cb_get_newstack(void)
352 {
353 	caddr_t newstack;
354 
355 	CB_VENTRY(cb_get_newstack);
356 	newstack = map_free_phys((caddr_t)CB_STACK_VIRT,
357 	    CB_STACK_SIZE, "new stack");
358 	if (newstack != (caddr_t)CB_STACK_VIRT)
359 		return (ERR);
360 	return (0);
361 }
362 
363 
364 /*
365  * since kernel phys pages span most of the installed memory range,
366  * some statefile buffer pages will likely clash with the kernel
367  * and need to be moved before kernel pages are restored; a list
368  * of buf phys page numbers is created here and later updated as
369  * buf pages are moved
370  *
371  * sets globals:
372  *	sfile.buf_map
373  *	tracking_init
374  */
375 int
cb_tracking_setup(void)376 cb_tracking_setup(void)
377 {
378 	pfn_t ppn, lppn;
379 	uint_t *imap;
380 	caddr_t newvirt;
381 	size_t size;
382 	int pages;
383 
384 	CB_VENTRY(cb_tracking_setup);
385 
386 	pages = mmu_btop(sfile.size);
387 	size = PAGE_ROUNDUP(pages * sizeof (*imap));
388 	newvirt = map_free_phys(high_virt, size, "buf tracking");
389 	if (newvirt != high_virt)
390 		return (ERR);
391 	sfile.buf_map = (uint_t *)newvirt;
392 	high_virt += size;
393 
394 	/*
395 	 * create identity map of sfile.buf phys pages
396 	 */
397 	imap = sfile.buf_map;
398 	lppn = sfile.low_ppn + pages;
399 	for (ppn = sfile.low_ppn; ppn < lppn; ppn++, imap++)
400 		*imap = (uint_t)ppn;
401 	tracking_init = 1;
402 
403 	return (0);
404 }
405 
406 
407 /*
408  * get "available" prop from /memory node
409  *
410  * sets globals:
411  *	cb_physavail
412  */
413 int
cb_get_physavail(void)414 cb_get_physavail(void)
415 {
416 	int len, glen, scnt, need, space;
417 	char *str, *pdev, *mem_prop;
418 	pnode_t mem_node;
419 	physaddr_t phys;
420 	pgcnt_t pages;
421 	arange_t *arp;
422 	pphav_t *pap;
423 	size_t size;
424 	pfn_t ppn;
425 	int err;
426 
427 	str = "cb_get_physavail";
428 	CB_VPRINTF((ent_fmt, str, entry));
429 
430 	/*
431 	 * first move cprboot pages off the physavail list
432 	 */
433 	size = PAGE_ROUNDUP((uintptr_t)_end) - (uintptr_t)_start;
434 	ppn = cpr_vatopfn((caddr_t)_start);
435 	phys = PN_TO_ADDR(ppn);
436 	err = prom_claim_phys(size, phys);
437 	CB_VPRINTF(("    text/data claim (0x%lx - 0x%lx) = %d\n",
438 	    ppn, ppn + mmu_btop(size) - 1, err));
439 	if (err)
440 		return (ERR);
441 
442 	pdev = "/memory";
443 	mem_node = prom_finddevice(pdev);
444 	if (mem_node == OBP_BADNODE) {
445 		prom_printf("%s: cant find \"%s\" node\n", str, pdev);
446 		return (ERR);
447 	}
448 	mem_prop = "available";
449 
450 	/*
451 	 * prop data is treated as a struct array;
452 	 * verify pabuf has enough room for the array
453 	 * in the original and converted forms
454 	 */
455 	len = prom_getproplen(mem_node, mem_prop);
456 	scnt = len / sizeof (*pap);
457 	need = len + (sizeof (*arp) * (scnt + 1));
458 	space = sizeof (pabuf);
459 	CB_VPRINTF(("    %s node 0x%x, len %d\n", pdev, mem_node, len));
460 	if (len == -1 || need > space) {
461 		prom_printf("\n%s: bad \"%s\" length %d, min %d, max %d\n",
462 		    str, mem_prop, len, need, space);
463 		return (ERR);
464 	}
465 
466 	/*
467 	 * read-in prop data and clear trailing space
468 	 */
469 	glen = prom_getprop(mem_node, mem_prop, pabuf);
470 	if (glen != len) {
471 		prom_printf("\n%s: 0x%x,%s: expected len %d, got %d\n",
472 		    str, mem_node, mem_prop, len, glen);
473 		return (ERR);
474 	}
475 	bzero(&pabuf[len], space - len);
476 
477 	/*
478 	 * convert the physavail list in place
479 	 * from (phys_base, phys_size) to (low_ppn, high_ppn)
480 	 */
481 	if (verbose)
482 		prom_printf("\nphysavail list:\n");
483 	cb_physavail = (arange_t *)pabuf;
484 	arp = cb_physavail + scnt - 1;
485 	pap = (pphav_t *)cb_physavail + scnt - 1;
486 	for (; scnt--; pap--, arp--) {
487 		pages = mmu_btop(pap->size);
488 		arp->low = ADDR_TO_PN(pap->base);
489 		arp->high = arp->low + pages - 1;
490 		if (verbose) {
491 			prom_printf("  %d: (0x%lx - 0x%lx),\tpages %ld\n",
492 			    (int)(arp - cb_physavail),
493 			    arp->low, arp->high, (arp->high - arp->low + 1));
494 		}
495 	}
496 
497 	return (0);
498 }
499 
500 
501 /*
502  * search for an available phys page,
503  * copy the old phys page to the new one
504  * and remap the virt page to the new phys
505  */
506 static int
move_page(caddr_t vaddr,pfn_t oldppn)507 move_page(caddr_t vaddr, pfn_t oldppn)
508 {
509 	physaddr_t oldphys, newphys;
510 	pfn_t newppn;
511 	int err;
512 
513 	newppn = find_apage();
514 	newphys = PN_TO_ADDR(newppn);
515 	oldphys = PN_TO_ADDR(oldppn);
516 	CB_VPRINTF(("    remap vaddr 0x%p, old 0x%lx/0x%llx,"
517 	    "	new 0x%lx/0x%llx\n",
518 	    (void *)vaddr, oldppn, oldphys, newppn, newphys));
519 	phys_xcopy(oldphys, newphys, MMU_PAGESIZE);
520 	err = prom_remap(MMU_PAGESIZE, vaddr, newphys);
521 	if (err)
522 		prom_printf("\nmove_page: remap error\n");
523 	return (err);
524 }
525 
526 
527 /*
528  * physically relocate any text/data pages that clash
529  * with the kernel; since we're already running on
530  * a new stack, the original stack area is skipped
531  */
532 int
cb_relocate(void)533 cb_relocate(void)
534 {
535 	int is_ostk, is_clash, clash_cnt, ok_cnt;
536 	char *str, *desc, *skip_fmt;
537 	caddr_t ostk_low, ostk_high;
538 	caddr_t virt, saddr, eaddr;
539 	pfn_t ppn;
540 
541 	str = "cb_relocate";
542 	CB_VPRINTF((ent_fmt, str, entry));
543 
544 	ostk_low  = (caddr_t)&estack - CB_STACK_SIZE;
545 	ostk_high = (caddr_t)&estack - MMU_PAGESIZE;
546 	saddr = (caddr_t)_start;
547 	eaddr = (caddr_t)PAGE_ROUNDUP((uintptr_t)_end);
548 
549 	install_remap();
550 
551 	skip_fmt = "    skip  vaddr 0x%p, clash=%d, %s\n";
552 	clash_cnt = ok_cnt = 0;
553 	ppn = cpr_vatopfn(saddr);
554 
555 	for (virt = saddr; virt < eaddr; virt += MMU_PAGESIZE, ppn++) {
556 		is_clash = (cb_bitop(ppn, CB_ISSET) != 0);
557 		if (is_clash)
558 			clash_cnt++;
559 		else
560 			ok_cnt++;
561 
562 		is_ostk = (virt >= ostk_low && virt <= ostk_high);
563 		if (is_ostk)
564 			desc = "orig stack";
565 		else
566 			desc = "text/data";
567 
568 		/*
569 		 * page logic:
570 		 *
571 		 * if (original stack page)
572 		 *	clash doesn't matter, just skip the page
573 		 * else (not original stack page)
574 		 * 	if (no clash)
575 		 *		setbit to avoid later alloc and overwrite
576 		 *	else (clash)
577 		 *		relocate phys page
578 		 */
579 		if (is_ostk) {
580 			CB_VPRINTF((skip_fmt, virt, is_clash, desc));
581 		} else if (is_clash == 0) {
582 			CB_VPRINTF((skip_fmt, virt, is_clash, desc));
583 			(void) cb_bitop(ppn, CB_SETBIT);
584 		} else if (move_page(virt, ppn))
585 			return (ERR);
586 	}
587 	CB_VPRINTF(("%s: total %d, clash %d, ok %d\n",
588 	    str, clash_cnt + ok_cnt, clash_cnt, ok_cnt));
589 
590 	/*
591 	 * free original stack area for reuse
592 	 */
593 	ppn = cpr_vatopfn(ostk_low);
594 	prom_free_phys(CB_STACK_SIZE, PN_TO_ADDR(ppn));
595 	CB_VPRINTF(("%s: free old stack (0x%lx - 0x%lx)\n",
596 	    str, ppn, ppn + mmu_btop(CB_STACK_SIZE) - 1));
597 
598 	return (0);
599 }
600