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 */
48int cb_nbitmaps;
49
50/*
51 * file scope
52 */
53static arange_t *cb_physavail;
54static char pabuf[PA_BUFSIZE];
55static caddr_t high_virt;
56
57static cbd_t cb_bmda[CPR_MAX_BMDESC];
58static int tracking_init;
59
60
61static int
62cb_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 */
90static void
91count_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 */
112static pfn_t
113search_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 */
155static pfn_t
156search_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 */
197pfn_t
198find_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 */
222static caddr_t
223map_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 */
267int
268cb_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 */
350int
351cb_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 */
375int
376cb_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 */
413int
414cb_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 */
506static int
507move_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 */
532int
533cb_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