xref: /titanic_51/usr/src/uts/i86pc/os/fakebop.c (revision 8793b36b40d14ad0a0fecc97738dc118a928f46c)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * This file contains the functionality that mimics the boot operations
29  * on SPARC systems or the old boot.bin/multiboot programs on x86 systems.
30  * The x86 kernel now does everything on its own.
31  */
32 
33 #include <sys/types.h>
34 #include <sys/bootconf.h>
35 #include <sys/bootsvcs.h>
36 #include <sys/bootinfo.h>
37 #include <sys/multiboot.h>
38 #include <sys/bootvfs.h>
39 #include <sys/bootprops.h>
40 #include <sys/varargs.h>
41 #include <sys/param.h>
42 #include <sys/machparam.h>
43 #include <sys/archsystm.h>
44 #include <sys/boot_console.h>
45 #include <sys/cmn_err.h>
46 #include <sys/systm.h>
47 #include <sys/promif.h>
48 #include <sys/archsystm.h>
49 #include <sys/x86_archext.h>
50 #include <sys/kobj.h>
51 #include <sys/privregs.h>
52 #include <sys/sysmacros.h>
53 #include <sys/ctype.h>
54 #ifdef __xpv
55 #include <sys/hypervisor.h>
56 #include <net/if.h>
57 #endif
58 #include <vm/kboot_mmu.h>
59 #include <vm/hat_pte.h>
60 #include <sys/dmar_acpi.h>
61 #include "acpi_fw.h"
62 
63 static int have_console = 0;	/* set once primitive console is initialized */
64 static char *boot_args = "";
65 
66 /*
67  * Debugging macros
68  */
69 static uint_t kbm_debug = 0;
70 #define	DBG_MSG(s)	{ if (kbm_debug) bop_printf(NULL, "%s", s); }
71 #define	DBG(x)		{ if (kbm_debug)			\
72 	bop_printf(NULL, "%s is %" PRIx64 "\n", #x, (uint64_t)(x));	\
73 	}
74 
75 #define	PUT_STRING(s) {				\
76 	char *cp;				\
77 	for (cp = (s); *cp; ++cp)		\
78 		bcons_putchar(*cp);		\
79 	}
80 
81 struct xboot_info *xbootp;	/* boot info from "glue" code in low memory */
82 bootops_t bootop;	/* simple bootops we'll pass on to kernel */
83 struct bsys_mem bm;
84 
85 static uintptr_t next_virt;	/* next available virtual address */
86 static paddr_t next_phys;	/* next available physical address from dboot */
87 static paddr_t high_phys = -(paddr_t)1;	/* last used physical address */
88 
89 /*
90  * buffer for vsnprintf for console I/O
91  */
92 #define	BUFFERSIZE	256
93 static char buffer[BUFFERSIZE];
94 /*
95  * stuff to store/report/manipulate boot property settings.
96  */
97 typedef struct bootprop {
98 	struct bootprop *bp_next;
99 	char *bp_name;
100 	uint_t bp_vlen;
101 	char *bp_value;
102 } bootprop_t;
103 
104 static bootprop_t *bprops = NULL;
105 static char *curr_page = NULL;		/* ptr to avail bprop memory */
106 static int curr_space = 0;		/* amount of memory at curr_page */
107 
108 #ifdef __xpv
109 start_info_t *xen_info;
110 shared_info_t *HYPERVISOR_shared_info;
111 #endif
112 
113 /*
114  * some allocator statistics
115  */
116 static ulong_t total_bop_alloc_scratch = 0;
117 static ulong_t total_bop_alloc_kernel = 0;
118 
119 static void build_firmware_properties(void);
120 
121 static int early_allocation = 1;
122 
123 /*
124  * Pointers to where System Resource Affinity Table (SRAT) and
125  * System Locality Information Table (SLIT) are mapped into virtual memory
126  */
127 struct srat	*srat_ptr = NULL;
128 struct slit	*slit_ptr = NULL;
129 
130 
131 /*
132  * Allocate aligned physical memory at boot time. This allocator allocates
133  * from the highest possible addresses. This avoids exhausting memory that
134  * would be useful for DMA buffers.
135  */
136 paddr_t
137 do_bop_phys_alloc(uint64_t size, uint64_t align)
138 {
139 	paddr_t	pa = 0;
140 	paddr_t	start;
141 	paddr_t	end;
142 	struct memlist	*ml = (struct memlist *)xbootp->bi_phys_install;
143 
144 	/*
145 	 * Be careful if high memory usage is limited in startup.c
146 	 * Since there are holes in the low part of the physical address
147 	 * space we can treat physmem as a pfn (not just a pgcnt) and
148 	 * get a conservative upper limit.
149 	 */
150 	if (physmem != 0 && high_phys > pfn_to_pa(physmem))
151 		high_phys = pfn_to_pa(physmem);
152 
153 	/*
154 	 * find the lowest or highest available memory in physinstalled
155 	 * On 32 bit avoid physmem above 4Gig if PAE isn't enabled
156 	 */
157 #if defined(__i386)
158 	if (xbootp->bi_use_pae == 0 && high_phys > FOUR_GIG)
159 		high_phys = FOUR_GIG;
160 #endif
161 
162 	/*
163 	 * find the highest available memory in physinstalled
164 	 */
165 	size = P2ROUNDUP(size, align);
166 	for (; ml; ml = ml->next) {
167 		start = P2ROUNDUP(ml->address, align);
168 		end = P2ALIGN(ml->address + ml->size, align);
169 		if (start < next_phys)
170 			start = P2ROUNDUP(next_phys, align);
171 		if (end > high_phys)
172 			end = P2ALIGN(high_phys, align);
173 
174 		if (end <= start)
175 			continue;
176 		if (end - start < size)
177 			continue;
178 
179 		/*
180 		 * Early allocations need to use low memory, since
181 		 * physmem might be further limited by bootenv.rc
182 		 */
183 		if (early_allocation) {
184 			if (pa == 0 || start < pa)
185 				pa = start;
186 		} else {
187 			if (end - size > pa)
188 				pa = end - size;
189 		}
190 	}
191 	if (pa != 0) {
192 		if (early_allocation)
193 			next_phys = pa + size;
194 		else
195 			high_phys = pa;
196 		return (pa);
197 	}
198 	bop_panic("do_bop_phys_alloc(0x%" PRIx64 ", 0x%" PRIx64
199 	    ") Out of memory\n", size, align);
200 	/*NOTREACHED*/
201 }
202 
203 static uintptr_t
204 alloc_vaddr(size_t size, paddr_t align)
205 {
206 	uintptr_t rv;
207 
208 	next_virt = P2ROUNDUP(next_virt, (uintptr_t)align);
209 	rv = (uintptr_t)next_virt;
210 	next_virt += size;
211 	return (rv);
212 }
213 
214 /*
215  * Allocate virtual memory. The size is always rounded up to a multiple
216  * of base pagesize.
217  */
218 
219 /*ARGSUSED*/
220 static caddr_t
221 do_bsys_alloc(bootops_t *bop, caddr_t virthint, size_t size, int align)
222 {
223 	paddr_t a = align;	/* same type as pa for masking */
224 	uint_t pgsize;
225 	paddr_t pa;
226 	uintptr_t va;
227 	ssize_t s;		/* the aligned size */
228 	uint_t level;
229 	uint_t is_kernel = (virthint != 0);
230 
231 	if (a < MMU_PAGESIZE)
232 		a = MMU_PAGESIZE;
233 	else if (!ISP2(a))
234 		prom_panic("do_bsys_alloc() incorrect alignment");
235 	size = P2ROUNDUP(size, MMU_PAGESIZE);
236 
237 	/*
238 	 * Use the next aligned virtual address if we weren't given one.
239 	 */
240 	if (virthint == NULL) {
241 		virthint = (caddr_t)alloc_vaddr(size, a);
242 		total_bop_alloc_scratch += size;
243 	} else {
244 		total_bop_alloc_kernel += size;
245 	}
246 
247 	/*
248 	 * allocate the physical memory
249 	 */
250 	pa = do_bop_phys_alloc(size, a);
251 
252 	/*
253 	 * Add the mappings to the page tables, try large pages first.
254 	 */
255 	va = (uintptr_t)virthint;
256 	s = size;
257 	level = 1;
258 	pgsize = xbootp->bi_use_pae ? TWO_MEG : FOUR_MEG;
259 	if (xbootp->bi_use_largepage && a == pgsize) {
260 		while (IS_P2ALIGNED(pa, pgsize) && IS_P2ALIGNED(va, pgsize) &&
261 		    s >= pgsize) {
262 			kbm_map(va, pa, level, is_kernel);
263 			va += pgsize;
264 			pa += pgsize;
265 			s -= pgsize;
266 		}
267 	}
268 
269 	/*
270 	 * Map remaining pages use small mappings
271 	 */
272 	level = 0;
273 	pgsize = MMU_PAGESIZE;
274 	while (s > 0) {
275 		kbm_map(va, pa, level, is_kernel);
276 		va += pgsize;
277 		pa += pgsize;
278 		s -= pgsize;
279 	}
280 	return (virthint);
281 }
282 
283 /*
284  * Free virtual memory - we'll just ignore these.
285  */
286 /*ARGSUSED*/
287 static void
288 do_bsys_free(bootops_t *bop, caddr_t virt, size_t size)
289 {
290 	bop_printf(NULL, "do_bsys_free(virt=0x%p, size=0x%lx) ignored\n",
291 	    (void *)virt, size);
292 }
293 
294 /*
295  * Old interface
296  */
297 /*ARGSUSED*/
298 static caddr_t
299 do_bsys_ealloc(
300 	bootops_t *bop,
301 	caddr_t virthint,
302 	size_t size,
303 	int align,
304 	int flags)
305 {
306 	prom_panic("unsupported call to BOP_EALLOC()\n");
307 	return (0);
308 }
309 
310 
311 static void
312 bsetprop(char *name, int nlen, void *value, int vlen)
313 {
314 	uint_t size;
315 	uint_t need_size;
316 	bootprop_t *b;
317 
318 	/*
319 	 * align the size to 16 byte boundary
320 	 */
321 	size = sizeof (bootprop_t) + nlen + 1 + vlen;
322 	size = (size + 0xf) & ~0xf;
323 	if (size > curr_space) {
324 		need_size = (size + (MMU_PAGEOFFSET)) & MMU_PAGEMASK;
325 		curr_page = do_bsys_alloc(NULL, 0, need_size, MMU_PAGESIZE);
326 		curr_space = need_size;
327 	}
328 
329 	/*
330 	 * use a bootprop_t at curr_page and link into list
331 	 */
332 	b = (bootprop_t *)curr_page;
333 	curr_page += sizeof (bootprop_t);
334 	curr_space -=  sizeof (bootprop_t);
335 	b->bp_next = bprops;
336 	bprops = b;
337 
338 	/*
339 	 * follow by name and ending zero byte
340 	 */
341 	b->bp_name = curr_page;
342 	bcopy(name, curr_page, nlen);
343 	curr_page += nlen;
344 	*curr_page++ = 0;
345 	curr_space -= nlen + 1;
346 
347 	/*
348 	 * copy in value, but no ending zero byte
349 	 */
350 	b->bp_value = curr_page;
351 	b->bp_vlen = vlen;
352 	if (vlen > 0) {
353 		bcopy(value, curr_page, vlen);
354 		curr_page += vlen;
355 		curr_space -= vlen;
356 	}
357 
358 	/*
359 	 * align new values of curr_page, curr_space
360 	 */
361 	while (curr_space & 0xf) {
362 		++curr_page;
363 		--curr_space;
364 	}
365 }
366 
367 static void
368 bsetprops(char *name, char *value)
369 {
370 	bsetprop(name, strlen(name), value, strlen(value) + 1);
371 }
372 
373 static void
374 bsetprop64(char *name, uint64_t value)
375 {
376 	bsetprop(name, strlen(name), (void *)&value, sizeof (value));
377 }
378 
379 static void
380 bsetpropsi(char *name, int value)
381 {
382 	char prop_val[32];
383 
384 	(void) snprintf(prop_val, sizeof (prop_val), "%d", value);
385 	bsetprops(name, prop_val);
386 }
387 
388 /*
389  * to find the size of the buffer to allocate
390  */
391 /*ARGSUSED*/
392 int
393 do_bsys_getproplen(bootops_t *bop, const char *name)
394 {
395 	bootprop_t *b;
396 
397 	for (b = bprops; b; b = b->bp_next) {
398 		if (strcmp(name, b->bp_name) != 0)
399 			continue;
400 		return (b->bp_vlen);
401 	}
402 	return (-1);
403 }
404 
405 /*
406  * get the value associated with this name
407  */
408 /*ARGSUSED*/
409 int
410 do_bsys_getprop(bootops_t *bop, const char *name, void *value)
411 {
412 	bootprop_t *b;
413 
414 	for (b = bprops; b; b = b->bp_next) {
415 		if (strcmp(name, b->bp_name) != 0)
416 			continue;
417 		bcopy(b->bp_value, value, b->bp_vlen);
418 		return (0);
419 	}
420 	return (-1);
421 }
422 
423 /*
424  * get the name of the next property in succession from the standalone
425  */
426 /*ARGSUSED*/
427 static char *
428 do_bsys_nextprop(bootops_t *bop, char *name)
429 {
430 	bootprop_t *b;
431 
432 	/*
433 	 * A null name is a special signal for the 1st boot property
434 	 */
435 	if (name == NULL || strlen(name) == 0) {
436 		if (bprops == NULL)
437 			return (NULL);
438 		return (bprops->bp_name);
439 	}
440 
441 	for (b = bprops; b; b = b->bp_next) {
442 		if (name != b->bp_name)
443 			continue;
444 		b = b->bp_next;
445 		if (b == NULL)
446 			return (NULL);
447 		return (b->bp_name);
448 	}
449 	return (NULL);
450 }
451 
452 /*
453  * Parse numeric value from a string. Understands decimal, hex, octal, - and ~
454  */
455 static int
456 parse_value(char *p, uint64_t *retval)
457 {
458 	int adjust = 0;
459 	uint64_t tmp = 0;
460 	int digit;
461 	int radix = 10;
462 
463 	*retval = 0;
464 	if (*p == '-' || *p == '~')
465 		adjust = *p++;
466 
467 	if (*p == '0') {
468 		++p;
469 		if (*p == 0)
470 			return (0);
471 		if (*p == 'x' || *p == 'X') {
472 			radix = 16;
473 			++p;
474 		} else {
475 			radix = 8;
476 			++p;
477 		}
478 	}
479 	while (*p) {
480 		if ('0' <= *p && *p <= '9')
481 			digit = *p - '0';
482 		else if ('a' <= *p && *p <= 'f')
483 			digit = 10 + *p - 'a';
484 		else if ('A' <= *p && *p <= 'F')
485 			digit = 10 + *p - 'A';
486 		else
487 			return (-1);
488 		if (digit >= radix)
489 			return (-1);
490 		tmp = tmp * radix + digit;
491 		++p;
492 	}
493 	if (adjust == '-')
494 		tmp = -tmp;
495 	else if (adjust == '~')
496 		tmp = ~tmp;
497 	*retval = tmp;
498 	return (0);
499 }
500 
501 /*
502  * 2nd part of building the table of boot properties. This includes:
503  * - values from /boot/solaris/bootenv.rc (ie. eeprom(1m) values)
504  *
505  * lines look like one of:
506  * ^$
507  * ^# comment till end of line
508  * setprop name 'value'
509  * setprop name value
510  * setprop name "value"
511  *
512  * we do single character I/O since this is really just looking at memory
513  */
514 void
515 boot_prop_finish(void)
516 {
517 	int fd;
518 	char *line;
519 	int c;
520 	int bytes_read;
521 	char *name;
522 	int n_len;
523 	char *value;
524 	int v_len;
525 	char *inputdev;	/* these override the command line if serial ports */
526 	char *outputdev;
527 	char *consoledev;
528 	uint64_t lvalue;
529 	int use_xencons = 0;
530 
531 #ifdef __xpv
532 	if (!DOMAIN_IS_INITDOMAIN(xen_info))
533 		use_xencons = 1;
534 #endif /* __xpv */
535 
536 	DBG_MSG("Opening /boot/solaris/bootenv.rc\n");
537 	fd = BRD_OPEN(bfs_ops, "/boot/solaris/bootenv.rc", 0);
538 	DBG(fd);
539 
540 	line = do_bsys_alloc(NULL, NULL, MMU_PAGESIZE, MMU_PAGESIZE);
541 	while (fd >= 0) {
542 
543 		/*
544 		 * get a line
545 		 */
546 		for (c = 0; ; ++c) {
547 			bytes_read = BRD_READ(bfs_ops, fd, line + c, 1);
548 			if (bytes_read == 0) {
549 				if (c == 0)
550 					goto done;
551 				break;
552 			}
553 			if (line[c] == '\n')
554 				break;
555 		}
556 		line[c] = 0;
557 
558 		/*
559 		 * ignore comment lines
560 		 */
561 		c = 0;
562 		while (ISSPACE(line[c]))
563 			++c;
564 		if (line[c] == '#' || line[c] == 0)
565 			continue;
566 
567 		/*
568 		 * must have "setprop " or "setprop\t"
569 		 */
570 		if (strncmp(line + c, "setprop ", 8) != 0 &&
571 		    strncmp(line + c, "setprop\t", 8) != 0)
572 			continue;
573 		c += 8;
574 		while (ISSPACE(line[c]))
575 			++c;
576 		if (line[c] == 0)
577 			continue;
578 
579 		/*
580 		 * gather up the property name
581 		 */
582 		name = line + c;
583 		n_len = 0;
584 		while (line[c] && !ISSPACE(line[c]))
585 			++n_len, ++c;
586 
587 		/*
588 		 * gather up the value, if any
589 		 */
590 		value = "";
591 		v_len = 0;
592 		while (ISSPACE(line[c]))
593 			++c;
594 		if (line[c] != 0) {
595 			value = line + c;
596 			while (line[c] && !ISSPACE(line[c]))
597 				++v_len, ++c;
598 		}
599 
600 		if (v_len >= 2 && value[0] == value[v_len - 1] &&
601 		    (value[0] == '\'' || value[0] == '"')) {
602 			++value;
603 			v_len -= 2;
604 		}
605 		name[n_len] = 0;
606 		if (v_len > 0)
607 			value[v_len] = 0;
608 		else
609 			continue;
610 
611 		/*
612 		 * ignore "boot-file" property, it's now meaningless
613 		 */
614 		if (strcmp(name, "boot-file") == 0)
615 			continue;
616 		if (strcmp(name, "boot-args") == 0 &&
617 		    strlen(boot_args) > 0)
618 			continue;
619 
620 		/*
621 		 * If a property was explicitly set on the command line
622 		 * it will override a setting in bootenv.rc
623 		 */
624 		if (do_bsys_getproplen(NULL, name) > 0)
625 			continue;
626 
627 		bsetprop(name, n_len, value, v_len + 1);
628 	}
629 done:
630 	if (fd >= 0)
631 		BRD_CLOSE(bfs_ops, fd);
632 
633 	/*
634 	 * Check if we have to limit the boot time allocator
635 	 */
636 	if (do_bsys_getproplen(NULL, "physmem") != -1 &&
637 	    do_bsys_getprop(NULL, "physmem", line) >= 0 &&
638 	    parse_value(line, &lvalue) != -1) {
639 		if (0 < lvalue && (lvalue < physmem || physmem == 0)) {
640 			physmem = (pgcnt_t)lvalue;
641 			DBG(physmem);
642 		}
643 	}
644 	early_allocation = 0;
645 
646 	/*
647 	 * check to see if we have to override the default value of the console
648 	 */
649 	if (!use_xencons) {
650 		inputdev = line;
651 		v_len = do_bsys_getproplen(NULL, "input-device");
652 		if (v_len > 0)
653 			(void) do_bsys_getprop(NULL, "input-device", inputdev);
654 		else
655 			v_len = 0;
656 		inputdev[v_len] = 0;
657 
658 		outputdev = inputdev + v_len + 1;
659 		v_len = do_bsys_getproplen(NULL, "output-device");
660 		if (v_len > 0)
661 			(void) do_bsys_getprop(NULL, "output-device",
662 			    outputdev);
663 		else
664 			v_len = 0;
665 		outputdev[v_len] = 0;
666 
667 		consoledev = outputdev + v_len + 1;
668 		v_len = do_bsys_getproplen(NULL, "console");
669 		if (v_len > 0)
670 			(void) do_bsys_getprop(NULL, "console", consoledev);
671 		else
672 			v_len = 0;
673 		consoledev[v_len] = 0;
674 		bcons_init2(inputdev, outputdev, consoledev);
675 	} else {
676 		/*
677 		 * Ensure console property exists
678 		 * If not create it as "hypervisor"
679 		 */
680 		v_len = do_bsys_getproplen(NULL, "console");
681 		if (v_len < 0)
682 			bsetprops("console", "hypervisor");
683 		inputdev = outputdev = consoledev = "hypervisor";
684 		bcons_init2(inputdev, outputdev, consoledev);
685 	}
686 
687 	if (strstr((char *)xbootp->bi_cmdline, "prom_debug") || kbm_debug) {
688 		value = line;
689 		bop_printf(NULL, "\nBoot properties:\n");
690 		name = "";
691 		while ((name = do_bsys_nextprop(NULL, name)) != NULL) {
692 			bop_printf(NULL, "\t0x%p %s = ", (void *)name, name);
693 			(void) do_bsys_getprop(NULL, name, value);
694 			v_len = do_bsys_getproplen(NULL, name);
695 			bop_printf(NULL, "len=%d ", v_len);
696 			value[v_len] = 0;
697 			bop_printf(NULL, "%s\n", value);
698 		}
699 	}
700 }
701 
702 /*
703  * print formatted output
704  */
705 /*PRINTFLIKE2*/
706 /*ARGSUSED*/
707 void
708 bop_printf(bootops_t *bop, const char *fmt, ...)
709 {
710 	va_list	ap;
711 
712 	if (have_console == 0)
713 		return;
714 
715 	va_start(ap, fmt);
716 	(void) vsnprintf(buffer, BUFFERSIZE, fmt, ap);
717 	va_end(ap);
718 	PUT_STRING(buffer);
719 }
720 
721 /*
722  * Another panic() variant; this one can be used even earlier during boot than
723  * prom_panic().
724  */
725 /*PRINTFLIKE1*/
726 void
727 bop_panic(const char *fmt, ...)
728 {
729 	va_list ap;
730 
731 	va_start(ap, fmt);
732 	bop_printf(NULL, fmt, ap);
733 	va_end(ap);
734 
735 	bop_printf(NULL, "\nPress any key to reboot.\n");
736 	(void) bcons_getchar();
737 	bop_printf(NULL, "Resetting...\n");
738 	pc_reset();
739 }
740 
741 /*
742  * Do a real mode interrupt BIOS call
743  */
744 typedef struct bios_regs {
745 	unsigned short ax, bx, cx, dx, si, di, bp, es, ds;
746 } bios_regs_t;
747 typedef int (*bios_func_t)(int, bios_regs_t *);
748 
749 /*ARGSUSED*/
750 static void
751 do_bsys_doint(bootops_t *bop, int intnum, struct bop_regs *rp)
752 {
753 #if defined(__xpv)
754 	prom_panic("unsupported call to BOP_DOINT()\n");
755 #else	/* __xpv */
756 	static int firsttime = 1;
757 	bios_func_t bios_func = (bios_func_t)(void *)(uintptr_t)0x5000;
758 	bios_regs_t br;
759 
760 	/*
761 	 * The first time we do this, we have to copy the pre-packaged
762 	 * low memory bios call code image into place.
763 	 */
764 	if (firsttime) {
765 		extern char bios_image[];
766 		extern uint32_t bios_size;
767 
768 		bcopy(bios_image, (void *)bios_func, bios_size);
769 		firsttime = 0;
770 	}
771 
772 	br.ax = rp->eax.word.ax;
773 	br.bx = rp->ebx.word.bx;
774 	br.cx = rp->ecx.word.cx;
775 	br.dx = rp->edx.word.dx;
776 	br.bp = rp->ebp.word.bp;
777 	br.si = rp->esi.word.si;
778 	br.di = rp->edi.word.di;
779 	br.ds = rp->ds;
780 	br.es = rp->es;
781 
782 	DBG_MSG("Doing BIOS call...");
783 	rp->eflags = bios_func(intnum, &br);
784 	DBG_MSG("done\n");
785 
786 	rp->eax.word.ax = br.ax;
787 	rp->ebx.word.bx = br.bx;
788 	rp->ecx.word.cx = br.cx;
789 	rp->edx.word.dx = br.dx;
790 	rp->ebp.word.bp = br.bp;
791 	rp->esi.word.si = br.si;
792 	rp->edi.word.di = br.di;
793 	rp->ds = br.ds;
794 	rp->es = br.es;
795 #endif /* __xpv */
796 }
797 
798 static struct boot_syscalls bop_sysp = {
799 	bcons_getchar,
800 	bcons_putchar,
801 	bcons_ischar,
802 };
803 
804 static char *whoami;
805 
806 #define	BUFLEN	64
807 
808 #if defined(__xpv)
809 
810 static char namebuf[32];
811 
812 static void
813 xen_parse_props(char *s, char *prop_map[], int n_prop)
814 {
815 	char **prop_name = prop_map;
816 	char *cp = s, *scp;
817 
818 	do {
819 		scp = cp;
820 		while ((*cp != NULL) && (*cp != ':'))
821 			cp++;
822 
823 		if ((scp != cp) && (*prop_name != NULL)) {
824 			*cp = NULL;
825 			bsetprops(*prop_name, scp);
826 		}
827 
828 		cp++;
829 		prop_name++;
830 		n_prop--;
831 	} while (n_prop > 0);
832 }
833 
834 #define	VBDPATHLEN	64
835 
836 /*
837  * parse the 'xpv-root' property to create properties used by
838  * ufs_mountroot.
839  */
840 static void
841 xen_vbdroot_props(char *s)
842 {
843 	char vbdpath[VBDPATHLEN] = "/xpvd/xdf@";
844 	const char lnamefix[] = "/dev/dsk/c0d";
845 	char *pnp;
846 	char *prop_p;
847 	char mi;
848 	short minor;
849 	long addr = 0;
850 
851 	pnp = vbdpath + strlen(vbdpath);
852 	prop_p = s + strlen(lnamefix);
853 	while ((*prop_p != '\0') && (*prop_p != 's') && (*prop_p != 'p'))
854 		addr = addr * 10 + *prop_p++ - '0';
855 	(void) snprintf(pnp, VBDPATHLEN, "%lx", addr);
856 	pnp = vbdpath + strlen(vbdpath);
857 	if (*prop_p == 's')
858 		mi = 'a';
859 	else if (*prop_p == 'p')
860 		mi = 'q';
861 	else
862 		ASSERT(0); /* shouldn't be here */
863 	prop_p++;
864 	ASSERT(*prop_p != '\0');
865 	if (ISDIGIT(*prop_p)) {
866 		minor = *prop_p - '0';
867 		prop_p++;
868 		if (ISDIGIT(*prop_p)) {
869 			minor = minor * 10 + *prop_p - '0';
870 		}
871 	} else {
872 		/* malformed root path, use 0 as default */
873 		minor = 0;
874 	}
875 	ASSERT(minor < 16); /* at most 16 partitions */
876 	mi += minor;
877 	*pnp++ = ':';
878 	*pnp++ = mi;
879 	*pnp++ = '\0';
880 	bsetprops("fstype", "ufs");
881 	bsetprops("bootpath", vbdpath);
882 
883 	DBG_MSG("VBD bootpath set to ");
884 	DBG_MSG(vbdpath);
885 	DBG_MSG("\n");
886 }
887 
888 /*
889  * parse the xpv-nfsroot property to create properties used by
890  * nfs_mountroot.
891  */
892 static void
893 xen_nfsroot_props(char *s)
894 {
895 	char *prop_map[] = {
896 		BP_SERVER_IP,	/* server IP address */
897 		BP_SERVER_NAME,	/* server hostname */
898 		BP_SERVER_PATH,	/* root path */
899 	};
900 	int n_prop = sizeof (prop_map) / sizeof (prop_map[0]);
901 
902 	bsetprop("fstype", 6, "nfs", 4);
903 
904 	xen_parse_props(s, prop_map, n_prop);
905 
906 	/*
907 	 * If a server name wasn't specified, use a default.
908 	 */
909 	if (do_bsys_getproplen(NULL, BP_SERVER_NAME) == -1)
910 		bsetprops(BP_SERVER_NAME, "unknown");
911 }
912 
913 /*
914  * Extract our IP address, etc. from the "xpv-ip" property.
915  */
916 static void
917 xen_ip_props(char *s)
918 {
919 	char *prop_map[] = {
920 		BP_HOST_IP,		/* IP address */
921 		NULL,			/* NFS server IP address (ignored in */
922 					/* favour of xpv-nfsroot) */
923 		BP_ROUTER_IP,		/* IP gateway */
924 		BP_SUBNET_MASK,		/* IP subnet mask */
925 		"xpv-hostname",		/* hostname (ignored) */
926 		BP_NETWORK_INTERFACE,	/* interface name */
927 		"xpv-hcp",		/* host configuration protocol */
928 	};
929 	int n_prop = sizeof (prop_map) / sizeof (prop_map[0]);
930 	char ifname[IFNAMSIZ];
931 
932 	xen_parse_props(s, prop_map, n_prop);
933 
934 	/*
935 	 * A Linux dom0 administrator expects all interfaces to be
936 	 * called "ethX", which is not the case here.
937 	 *
938 	 * If the interface name specified is "eth0", presume that
939 	 * this is really intended to be "xnf0" (the first domU ->
940 	 * dom0 interface for this domain).
941 	 */
942 	if ((do_bsys_getprop(NULL, BP_NETWORK_INTERFACE, ifname) == 0) &&
943 	    (strcmp("eth0", ifname) == 0)) {
944 		bsetprops(BP_NETWORK_INTERFACE, "xnf0");
945 		bop_printf(NULL,
946 		    "network interface name 'eth0' replaced with 'xnf0'\n");
947 	}
948 }
949 
950 #else	/* __xpv */
951 
952 static void
953 setup_rarp_props(struct sol_netinfo *sip)
954 {
955 	char buf[BUFLEN];	/* to hold ip/mac addrs */
956 	uint8_t *val;
957 
958 	val = (uint8_t *)&sip->sn_ciaddr;
959 	(void) snprintf(buf, BUFLEN, "%d.%d.%d.%d",
960 	    val[0], val[1], val[2], val[3]);
961 	bsetprops(BP_HOST_IP, buf);
962 
963 	val = (uint8_t *)&sip->sn_siaddr;
964 	(void) snprintf(buf, BUFLEN, "%d.%d.%d.%d",
965 	    val[0], val[1], val[2], val[3]);
966 	bsetprops(BP_SERVER_IP, buf);
967 
968 	if (sip->sn_giaddr != 0) {
969 		val = (uint8_t *)&sip->sn_giaddr;
970 		(void) snprintf(buf, BUFLEN, "%d.%d.%d.%d",
971 		    val[0], val[1], val[2], val[3]);
972 		bsetprops(BP_ROUTER_IP, buf);
973 	}
974 
975 	if (sip->sn_netmask != 0) {
976 		val = (uint8_t *)&sip->sn_netmask;
977 		(void) snprintf(buf, BUFLEN, "%d.%d.%d.%d",
978 		    val[0], val[1], val[2], val[3]);
979 		bsetprops(BP_SUBNET_MASK, buf);
980 	}
981 
982 	if (sip->sn_mactype != 4 || sip->sn_maclen != 6) {
983 		bop_printf(NULL, "unsupported mac type %d, mac len %d\n",
984 		    sip->sn_mactype, sip->sn_maclen);
985 	} else {
986 		val = sip->sn_macaddr;
987 		(void) snprintf(buf, BUFLEN, "%x:%x:%x:%x:%x:%x",
988 		    val[0], val[1], val[2], val[3], val[4], val[5]);
989 		bsetprops(BP_BOOT_MAC, buf);
990 	}
991 }
992 
993 #endif	/* __xpv */
994 
995 /*
996  * 1st pass at building the table of boot properties. This includes:
997  * - values set on the command line: -B a=x,b=y,c=z ....
998  * - known values we just compute (ie. from xbootp)
999  * - values from /boot/solaris/bootenv.rc (ie. eeprom(1m) values)
1000  *
1001  * the grub command line looked like:
1002  * kernel boot-file [-B prop=value[,prop=value]...] [boot-args]
1003  *
1004  * whoami is the same as boot-file
1005  */
1006 static void
1007 build_boot_properties(void)
1008 {
1009 	char *name;
1010 	int name_len;
1011 	char *value;
1012 	int value_len;
1013 	struct boot_modules *bm;
1014 	char *propbuf;
1015 	int quoted = 0;
1016 	int boot_arg_len;
1017 #ifndef __xpv
1018 	static int stdout_val = 0;
1019 	uchar_t boot_device;
1020 	char str[3];
1021 	multiboot_info_t *mbi;
1022 	int netboot;
1023 	struct sol_netinfo *sip;
1024 #endif
1025 
1026 	/*
1027 	 * These have to be done first, so that kobj_mount_root() works
1028 	 */
1029 	DBG_MSG("Building boot properties\n");
1030 	propbuf = do_bsys_alloc(NULL, NULL, MMU_PAGESIZE, 0);
1031 	DBG((uintptr_t)propbuf);
1032 	if (xbootp->bi_module_cnt > 0) {
1033 		bm = xbootp->bi_modules;
1034 		bsetprop64("ramdisk_start", (uint64_t)(uintptr_t)bm->bm_addr);
1035 		bsetprop64("ramdisk_end", (uint64_t)(uintptr_t)bm->bm_addr +
1036 		    bm->bm_size);
1037 	}
1038 
1039 	DBG_MSG("Parsing command line for boot properties\n");
1040 	value = xbootp->bi_cmdline;
1041 
1042 	/*
1043 	 * allocate memory to collect boot_args into
1044 	 */
1045 	boot_arg_len = strlen(xbootp->bi_cmdline) + 1;
1046 	boot_args = do_bsys_alloc(NULL, NULL, boot_arg_len, MMU_PAGESIZE);
1047 	boot_args[0] = 0;
1048 	boot_arg_len = 0;
1049 
1050 #ifdef __xpv
1051 	/*
1052 	 * Xen puts a lot of device information in front of the kernel name
1053 	 * let's grab them and make them boot properties.  The first
1054 	 * string w/o an "=" in it will be the boot-file property.
1055 	 */
1056 	(void) strcpy(namebuf, "xpv-");
1057 	for (;;) {
1058 		/*
1059 		 * get to next property
1060 		 */
1061 		while (ISSPACE(*value))
1062 			++value;
1063 		name = value;
1064 		/*
1065 		 * look for an "="
1066 		 */
1067 		while (*value && !ISSPACE(*value) && *value != '=') {
1068 			value++;
1069 		}
1070 		if (*value != '=') { /* no "=" in the property */
1071 			value = name;
1072 			break;
1073 		}
1074 		name_len = value - name;
1075 		value_len = 0;
1076 		/*
1077 		 * skip over the "="
1078 		 */
1079 		value++;
1080 		while (value[value_len] && !ISSPACE(value[value_len])) {
1081 			++value_len;
1082 		}
1083 		/*
1084 		 * build property name with "xpv-" prefix
1085 		 */
1086 		if (name_len + 4 > 32) { /* skip if name too long */
1087 			value += value_len;
1088 			continue;
1089 		}
1090 		bcopy(name, &namebuf[4], name_len);
1091 		name_len += 4;
1092 		namebuf[name_len] = 0;
1093 		bcopy(value, propbuf, value_len);
1094 		propbuf[value_len] = 0;
1095 		bsetprops(namebuf, propbuf);
1096 
1097 		/*
1098 		 * xpv-root is set to the logical disk name of the xen
1099 		 * VBD when booting from a disk-based filesystem.
1100 		 */
1101 		if (strcmp(namebuf, "xpv-root") == 0)
1102 			xen_vbdroot_props(propbuf);
1103 		/*
1104 		 * While we're here, if we have a "xpv-nfsroot" property
1105 		 * then we need to set "fstype" to "nfs" so we mount
1106 		 * our root from the nfs server.  Also parse the xpv-nfsroot
1107 		 * property to create the properties that nfs_mountroot will
1108 		 * need to find the root and mount it.
1109 		 */
1110 		if (strcmp(namebuf, "xpv-nfsroot") == 0)
1111 			xen_nfsroot_props(propbuf);
1112 
1113 		if (strcmp(namebuf, "xpv-ip") == 0)
1114 			xen_ip_props(propbuf);
1115 		value += value_len;
1116 	}
1117 #endif
1118 
1119 	while (ISSPACE(*value))
1120 		++value;
1121 	/*
1122 	 * value now points at the boot-file
1123 	 */
1124 	value_len = 0;
1125 	while (value[value_len] && !ISSPACE(value[value_len]))
1126 		++value_len;
1127 	if (value_len > 0) {
1128 		whoami = propbuf;
1129 		bcopy(value, whoami, value_len);
1130 		whoami[value_len] = 0;
1131 		bsetprops("boot-file", whoami);
1132 		/*
1133 		 * strip leading path stuff from whoami, so running from
1134 		 * PXE/miniroot makes sense.
1135 		 */
1136 		if (strstr(whoami, "/platform/") != NULL)
1137 			whoami = strstr(whoami, "/platform/");
1138 		bsetprops("whoami", whoami);
1139 	}
1140 
1141 	/*
1142 	 * Values forcibly set boot properties on the command line via -B.
1143 	 * Allow use of quotes in values. Other stuff goes on kernel
1144 	 * command line.
1145 	 */
1146 	name = value + value_len;
1147 	while (*name != 0) {
1148 		/*
1149 		 * anything not " -B" is copied to the command line
1150 		 */
1151 		if (!ISSPACE(name[0]) || name[1] != '-' || name[2] != 'B') {
1152 			boot_args[boot_arg_len++] = *name;
1153 			boot_args[boot_arg_len] = 0;
1154 			++name;
1155 			continue;
1156 		}
1157 
1158 		/*
1159 		 * skip the " -B" and following white space
1160 		 */
1161 		name += 3;
1162 		while (ISSPACE(*name))
1163 			++name;
1164 		while (*name && !ISSPACE(*name)) {
1165 			value = strstr(name, "=");
1166 			if (value == NULL)
1167 				break;
1168 			name_len = value - name;
1169 			++value;
1170 			value_len = 0;
1171 			quoted = 0;
1172 			for (; ; ++value_len) {
1173 				if (!value[value_len])
1174 					break;
1175 
1176 				/*
1177 				 * is this value quoted?
1178 				 */
1179 				if (value_len == 0 &&
1180 				    (value[0] == '\'' || value[0] == '"')) {
1181 					quoted = value[0];
1182 					++value_len;
1183 				}
1184 
1185 				/*
1186 				 * In the quote accept any character,
1187 				 * but look for ending quote.
1188 				 */
1189 				if (quoted) {
1190 					if (value[value_len] == quoted)
1191 						quoted = 0;
1192 					continue;
1193 				}
1194 
1195 				/*
1196 				 * a comma or white space ends the value
1197 				 */
1198 				if (value[value_len] == ',' ||
1199 				    ISSPACE(value[value_len]))
1200 					break;
1201 			}
1202 
1203 			if (value_len == 0) {
1204 				bsetprop(name, name_len, "true", 5);
1205 			} else {
1206 				char *v = value;
1207 				int l = value_len;
1208 				if (v[0] == v[l - 1] &&
1209 				    (v[0] == '\'' || v[0] == '"')) {
1210 					++v;
1211 					l -= 2;
1212 				}
1213 				bcopy(v, propbuf, l);
1214 				propbuf[l] = '\0';
1215 				bsetprop(name, name_len, propbuf,
1216 				    l + 1);
1217 			}
1218 			name = value + value_len;
1219 			while (*name == ',')
1220 				++name;
1221 		}
1222 	}
1223 
1224 	/*
1225 	 * set boot-args property
1226 	 * 1275 name is bootargs, so set
1227 	 * that too
1228 	 */
1229 	bsetprops("boot-args", boot_args);
1230 	bsetprops("bootargs", boot_args);
1231 
1232 #ifndef __xpv
1233 	/*
1234 	 * set the BIOS boot device from GRUB
1235 	 */
1236 	netboot = 0;
1237 	mbi = xbootp->bi_mb_info;
1238 	if (mbi != NULL && mbi->flags & 0x2) {
1239 		boot_device = mbi->boot_device >> 24;
1240 		if (boot_device == 0x20)
1241 			netboot++;
1242 		str[0] = (boot_device >> 4) + '0';
1243 		str[1] = (boot_device & 0xf) + '0';
1244 		str[2] = 0;
1245 		bsetprops("bios-boot-device", str);
1246 	} else {
1247 		netboot = 1;
1248 	}
1249 
1250 	/*
1251 	 * In the netboot case, drives_info is overloaded with the dhcp ack.
1252 	 * This is not multiboot compliant and requires special pxegrub!
1253 	 */
1254 	if (netboot && mbi->drives_length != 0) {
1255 		sip = (struct sol_netinfo *)(uintptr_t)mbi->drives_addr;
1256 		if (sip->sn_infotype == SN_TYPE_BOOTP)
1257 			bsetprop("bootp-response", sizeof ("bootp-response"),
1258 			    (void *)(uintptr_t)mbi->drives_addr,
1259 			    mbi->drives_length);
1260 		else if (sip->sn_infotype == SN_TYPE_RARP)
1261 			setup_rarp_props(sip);
1262 	}
1263 	bsetprop("stdout", strlen("stdout"),
1264 	    &stdout_val, sizeof (stdout_val));
1265 #endif /* __xpv */
1266 
1267 	/*
1268 	 * more conjured up values for made up things....
1269 	 */
1270 #if defined(__xpv)
1271 	bsetprops("mfg-name", "i86xpv");
1272 	bsetprops("impl-arch-name", "i86xpv");
1273 #else
1274 	bsetprops("mfg-name", "i86pc");
1275 	bsetprops("impl-arch-name", "i86pc");
1276 #endif
1277 
1278 	/*
1279 	 * Build firmware-provided system properties
1280 	 */
1281 	build_firmware_properties();
1282 
1283 	/*
1284 	 * XXPV
1285 	 *
1286 	 * Find out what these are:
1287 	 * - cpuid_feature_ecx_include
1288 	 * - cpuid_feature_ecx_exclude
1289 	 * - cpuid_feature_edx_include
1290 	 * - cpuid_feature_edx_exclude
1291 	 *
1292 	 * Find out what these are in multiboot:
1293 	 * - netdev-path
1294 	 * - fstype
1295 	 */
1296 }
1297 
1298 #ifdef __xpv
1299 /*
1300  * Under the Hypervisor, memory usable for DMA may be scarce. One
1301  * very likely large pool of DMA friendly memory is occupied by
1302  * the boot_archive, as it was loaded by grub into low MFNs.
1303  *
1304  * Here we free up that memory by copying the boot archive to what are
1305  * likely higher MFN pages and then swapping the mfn/pfn mappings.
1306  */
1307 #define	PFN_2GIG	0x80000
1308 static void
1309 relocate_boot_archive(void)
1310 {
1311 	mfn_t max_mfn = HYPERVISOR_memory_op(XENMEM_maximum_ram_page, NULL);
1312 	struct boot_modules *bm = xbootp->bi_modules;
1313 	uintptr_t va;
1314 	pfn_t va_pfn;
1315 	mfn_t va_mfn;
1316 	caddr_t copy;
1317 	pfn_t copy_pfn;
1318 	mfn_t copy_mfn;
1319 	size_t	len;
1320 	int slop;
1321 	int total = 0;
1322 	int relocated = 0;
1323 	int mmu_update_return;
1324 	mmu_update_t t[2];
1325 	x86pte_t pte;
1326 
1327 	/*
1328 	 * If all MFN's are below 2Gig, don't bother doing this.
1329 	 */
1330 	if (max_mfn < PFN_2GIG)
1331 		return;
1332 	if (xbootp->bi_module_cnt < 1) {
1333 		DBG_MSG("no boot_archive!");
1334 		return;
1335 	}
1336 
1337 	DBG_MSG("moving boot_archive to high MFN memory\n");
1338 	va = (uintptr_t)bm->bm_addr;
1339 	len = bm->bm_size;
1340 	slop = va & MMU_PAGEOFFSET;
1341 	if (slop) {
1342 		va += MMU_PAGESIZE - slop;
1343 		len -= MMU_PAGESIZE - slop;
1344 	}
1345 	len = P2ALIGN(len, MMU_PAGESIZE);
1346 
1347 	/*
1348 	 * Go through all boot_archive pages, swapping any low MFN pages
1349 	 * with memory at next_phys.
1350 	 */
1351 	while (len != 0) {
1352 		++total;
1353 		va_pfn = mmu_btop(va - ONE_GIG);
1354 		va_mfn = mfn_list[va_pfn];
1355 		if (mfn_list[va_pfn] < PFN_2GIG) {
1356 			copy = kbm_remap_window(next_phys, 1);
1357 			bcopy((void *)va, copy, MMU_PAGESIZE);
1358 			copy_pfn = mmu_btop(next_phys);
1359 			copy_mfn = mfn_list[copy_pfn];
1360 
1361 			pte = mfn_to_ma(copy_mfn) | PT_NOCONSIST | PT_VALID;
1362 			if (HYPERVISOR_update_va_mapping(va, pte,
1363 			    UVMF_INVLPG | UVMF_LOCAL))
1364 				bop_panic("relocate_boot_archive():  "
1365 				    "HYPERVISOR_update_va_mapping() failed");
1366 
1367 			mfn_list[va_pfn] = copy_mfn;
1368 			mfn_list[copy_pfn] = va_mfn;
1369 
1370 			t[0].ptr = mfn_to_ma(copy_mfn) | MMU_MACHPHYS_UPDATE;
1371 			t[0].val = va_pfn;
1372 			t[1].ptr = mfn_to_ma(va_mfn) | MMU_MACHPHYS_UPDATE;
1373 			t[1].val = copy_pfn;
1374 			if (HYPERVISOR_mmu_update(t, 2, &mmu_update_return,
1375 			    DOMID_SELF) != 0 || mmu_update_return != 2)
1376 				bop_panic("relocate_boot_archive():  "
1377 				    "HYPERVISOR_mmu_update() failed");
1378 
1379 			next_phys += MMU_PAGESIZE;
1380 			++relocated;
1381 		}
1382 		len -= MMU_PAGESIZE;
1383 		va += MMU_PAGESIZE;
1384 	}
1385 	DBG_MSG("Relocated pages:\n");
1386 	DBG(relocated);
1387 	DBG_MSG("Out of total pages:\n");
1388 	DBG(total);
1389 }
1390 #endif /* __xpv */
1391 
1392 #if !defined(__xpv)
1393 /*
1394  * Install a temporary IDT that lets us catch errors in the boot time code.
1395  * We shouldn't get any faults at all while this is installed, so we'll
1396  * just generate a traceback and exit.
1397  */
1398 #ifdef __amd64
1399 static const int bcode_sel = B64CODE_SEL;
1400 #else
1401 static const int bcode_sel = B32CODE_SEL;
1402 #endif
1403 
1404 /*
1405  * simple description of a stack frame (args are 32 bit only currently)
1406  */
1407 typedef struct bop_frame {
1408 	struct bop_frame *old_frame;
1409 	pc_t retaddr;
1410 	long arg[1];
1411 } bop_frame_t;
1412 
1413 void
1414 bop_traceback(bop_frame_t *frame)
1415 {
1416 	pc_t pc;
1417 	int cnt;
1418 	int a;
1419 	char *ksym;
1420 	ulong_t off;
1421 
1422 	bop_printf(NULL, "Stack traceback:\n");
1423 	for (cnt = 0; cnt < 30; ++cnt) {	/* up to 30 frames */
1424 		pc = frame->retaddr;
1425 		if (pc == 0)
1426 			break;
1427 		ksym = kobj_getsymname(pc, &off);
1428 		if (ksym)
1429 			bop_printf(NULL, "  %s+%lx", ksym, off);
1430 		else
1431 			bop_printf(NULL, "  0x%lx", pc);
1432 
1433 		frame = frame->old_frame;
1434 		if (frame == 0) {
1435 			bop_printf(NULL, "\n");
1436 			break;
1437 		}
1438 		for (a = 0; a < 6; ++a) {	/* try for 6 args */
1439 #if defined(__i386)
1440 			if ((void *)&frame->arg[a] == (void *)frame->old_frame)
1441 				break;
1442 			if (a == 0)
1443 				bop_printf(NULL, "(");
1444 			else
1445 				bop_printf(NULL, ",");
1446 			bop_printf(NULL, "0x%lx", frame->arg[a]);
1447 #endif
1448 		}
1449 		bop_printf(NULL, ")\n");
1450 	}
1451 }
1452 
1453 struct trapframe {
1454 	ulong_t frame_ptr;	/* %[er]bp pushed by our code */
1455 	ulong_t error_code;	/* optional */
1456 	ulong_t inst_ptr;
1457 	ulong_t code_seg;
1458 	ulong_t flags_reg;
1459 #ifdef __amd64
1460 	ulong_t stk_ptr;
1461 	ulong_t stk_seg;
1462 #endif
1463 };
1464 
1465 void
1466 bop_trap(struct trapframe *tf)
1467 {
1468 	bop_frame_t fakeframe;
1469 	static int depth = 0;
1470 
1471 	/*
1472 	 * Check for an infinite loop of traps.
1473 	 */
1474 	if (++depth > 2)
1475 		bop_panic("Nested trap");
1476 
1477 	/*
1478 	 * adjust the tf for optional error_code by detecting the code selector
1479 	 */
1480 	if (tf->code_seg != bcode_sel)
1481 		tf = (struct trapframe *)((uintptr_t)tf - sizeof (ulong_t));
1482 
1483 	bop_printf(NULL, "Unexpected trap\n");
1484 	bop_printf(NULL, "instruction pointer  0x%lx\n", tf->inst_ptr);
1485 	bop_printf(NULL, "error code, optional 0x%lx\n",
1486 	    tf->error_code & 0xffffffff);
1487 	bop_printf(NULL, "code segment         0x%lx\n", tf->code_seg & 0xffff);
1488 	bop_printf(NULL, "flags register       0x%lx\n", tf->flags_reg);
1489 #ifdef __amd64
1490 	bop_printf(NULL, "return %%rsp         0x%lx\n", tf->stk_ptr);
1491 	bop_printf(NULL, "return %%ss          0x%lx\n", tf->stk_seg & 0xffff);
1492 #endif
1493 	fakeframe.old_frame = (bop_frame_t *)tf->frame_ptr;
1494 	fakeframe.retaddr = (pc_t)tf->inst_ptr;
1495 	bop_printf(NULL, "Attempting stack backtrace:\n");
1496 	bop_traceback(&fakeframe);
1497 	bop_panic("unexpected trap in early boot");
1498 }
1499 
1500 extern void bop_trap_handler(void);
1501 
1502 static gate_desc_t *bop_idt;
1503 
1504 static desctbr_t bop_idt_info;
1505 
1506 static void
1507 bop_idt_init(void)
1508 {
1509 	int t;
1510 
1511 	bop_idt = (gate_desc_t *)
1512 	    do_bsys_alloc(NULL, NULL, MMU_PAGESIZE, MMU_PAGESIZE);
1513 	bzero(bop_idt, MMU_PAGESIZE);
1514 	for (t = 0; t < NIDT; ++t) {
1515 		set_gatesegd(&bop_idt[t], &bop_trap_handler, bcode_sel,
1516 		    SDT_SYSIGT, TRP_KPL);
1517 	}
1518 	bop_idt_info.dtr_limit = (NIDT * sizeof (gate_desc_t)) - 1;
1519 	bop_idt_info.dtr_base = (uintptr_t)bop_idt;
1520 	wr_idtr(&bop_idt_info);
1521 }
1522 #endif	/* !defined(__xpv) */
1523 
1524 /*
1525  * This is where we enter the kernel. It dummies up the boot_ops and
1526  * boot_syscalls vectors and jumps off to _kobj_boot()
1527  */
1528 void
1529 _start(struct xboot_info *xbp)
1530 {
1531 	bootops_t *bops = &bootop;
1532 	extern void _kobj_boot();
1533 
1534 	/*
1535 	 * 1st off - initialize the console for any error messages
1536 	 */
1537 	xbootp = xbp;
1538 #ifdef __xpv
1539 	HYPERVISOR_shared_info = (void *)xbootp->bi_shared_info;
1540 	xen_info = xbootp->bi_xen_start_info;
1541 #endif
1542 	bcons_init((void *)xbootp->bi_cmdline);
1543 	have_console = 1;
1544 
1545 	/*
1546 	 * enable debugging
1547 	 */
1548 	if (strstr((char *)xbootp->bi_cmdline, "kbm_debug"))
1549 		kbm_debug = 1;
1550 
1551 	DBG_MSG("\n\n*** Entered Solaris in _start() cmdline is: ");
1552 	DBG_MSG((char *)xbootp->bi_cmdline);
1553 	DBG_MSG("\n\n\n");
1554 
1555 	/*
1556 	 * physavail is no longer used by startup
1557 	 */
1558 	bm.physinstalled = xbp->bi_phys_install;
1559 	bm.pcimem = xbp->bi_pcimem;
1560 	bm.physavail = NULL;
1561 
1562 	/*
1563 	 * initialize the boot time allocator
1564 	 */
1565 	next_phys = xbootp->bi_next_paddr;
1566 	DBG(next_phys);
1567 	next_virt = (uintptr_t)xbootp->bi_next_vaddr;
1568 	DBG(next_virt);
1569 	DBG_MSG("Initializing boot time memory management...");
1570 #ifdef __xpv
1571 	{
1572 		xen_platform_parameters_t p;
1573 
1574 		/* This call shouldn't fail, dboot already did it once. */
1575 		(void) HYPERVISOR_xen_version(XENVER_platform_parameters, &p);
1576 		mfn_to_pfn_mapping = (pfn_t *)(xen_virt_start = p.virt_start);
1577 		DBG(xen_virt_start);
1578 	}
1579 #endif
1580 	kbm_init(xbootp);
1581 	DBG_MSG("done\n");
1582 
1583 	/*
1584 	 * Fill in the bootops vector
1585 	 */
1586 	bops->bsys_version = BO_VERSION;
1587 	bops->boot_mem = &bm;
1588 	bops->bsys_alloc = do_bsys_alloc;
1589 	bops->bsys_free = do_bsys_free;
1590 	bops->bsys_getproplen = do_bsys_getproplen;
1591 	bops->bsys_getprop = do_bsys_getprop;
1592 	bops->bsys_nextprop = do_bsys_nextprop;
1593 	bops->bsys_printf = bop_printf;
1594 	bops->bsys_doint = do_bsys_doint;
1595 
1596 	/*
1597 	 * BOP_EALLOC() is no longer needed
1598 	 */
1599 	bops->bsys_ealloc = do_bsys_ealloc;
1600 
1601 #ifdef __xpv
1602 	/*
1603 	 * On domain 0 we need to free up some physical memory that is
1604 	 * usable for DMA. Since GRUB loaded the boot_archive, it is
1605 	 * sitting in low MFN memory. We'll relocated the boot archive
1606 	 * pages to high PFN memory.
1607 	 */
1608 	if (DOMAIN_IS_INITDOMAIN(xen_info))
1609 		relocate_boot_archive();
1610 #endif
1611 
1612 #ifndef __xpv
1613 	/*
1614 	 * Install an IDT to catch early pagefaults (shouldn't have any).
1615 	 * Also needed for kmdb.
1616 	 */
1617 	bop_idt_init();
1618 #endif
1619 
1620 	/*
1621 	 * Start building the boot properties from the command line
1622 	 */
1623 	DBG_MSG("Initializing boot properties:\n");
1624 	build_boot_properties();
1625 
1626 	if (strstr((char *)xbootp->bi_cmdline, "prom_debug") || kbm_debug) {
1627 		char *name;
1628 		char *value;
1629 		char *cp;
1630 		int len;
1631 
1632 		value = do_bsys_alloc(NULL, NULL, MMU_PAGESIZE, MMU_PAGESIZE);
1633 		bop_printf(NULL, "\nBoot properties:\n");
1634 		name = "";
1635 		while ((name = do_bsys_nextprop(NULL, name)) != NULL) {
1636 			bop_printf(NULL, "\t0x%p %s = ", (void *)name, name);
1637 			(void) do_bsys_getprop(NULL, name, value);
1638 			len = do_bsys_getproplen(NULL, name);
1639 			bop_printf(NULL, "len=%d ", len);
1640 			value[len] = 0;
1641 			for (cp = value; *cp; ++cp) {
1642 				if (' ' <= *cp && *cp <= '~')
1643 					bop_printf(NULL, "%c", *cp);
1644 				else
1645 					bop_printf(NULL, "-0x%x-", *cp);
1646 			}
1647 			bop_printf(NULL, "\n");
1648 		}
1649 	}
1650 
1651 	/*
1652 	 * jump into krtld...
1653 	 */
1654 	_kobj_boot(&bop_sysp, NULL, bops, NULL);
1655 }
1656 
1657 
1658 /*ARGSUSED*/
1659 static caddr_t
1660 no_more_alloc(bootops_t *bop, caddr_t virthint, size_t size, int align)
1661 {
1662 	panic("Attempt to bsys_alloc() too late\n");
1663 	return (NULL);
1664 }
1665 
1666 /*ARGSUSED*/
1667 static void
1668 no_more_free(bootops_t *bop, caddr_t virt, size_t size)
1669 {
1670 	panic("Attempt to bsys_free() too late\n");
1671 }
1672 
1673 void
1674 bop_no_more_mem(void)
1675 {
1676 	DBG(total_bop_alloc_scratch);
1677 	DBG(total_bop_alloc_kernel);
1678 	bootops->bsys_alloc = no_more_alloc;
1679 	bootops->bsys_free = no_more_free;
1680 }
1681 
1682 
1683 #ifndef __xpv
1684 /*
1685  * Set ACPI firmware properties
1686  */
1687 
1688 static caddr_t
1689 vmap_phys(size_t length, paddr_t pa)
1690 {
1691 	paddr_t	start, end;
1692 	caddr_t	va;
1693 	size_t	len, page;
1694 
1695 	start = P2ALIGN(pa, MMU_PAGESIZE);
1696 	end = P2ROUNDUP(pa + length, MMU_PAGESIZE);
1697 	len = end - start;
1698 	va = (caddr_t)alloc_vaddr(len, MMU_PAGESIZE);
1699 	for (page = 0; page < len; page += MMU_PAGESIZE)
1700 		kbm_map((uintptr_t)va + page, start + page, 0, 0);
1701 	return (va + (pa & MMU_PAGEOFFSET));
1702 }
1703 
1704 static uint8_t
1705 checksum_table(uint8_t *tp, size_t len)
1706 {
1707 	uint8_t sum = 0;
1708 
1709 	while (len-- > 0)
1710 		sum += *tp++;
1711 
1712 	return (sum);
1713 }
1714 
1715 static int
1716 valid_rsdp(struct rsdp *rp)
1717 {
1718 
1719 	/* validate the V1.x checksum */
1720 	if (checksum_table((uint8_t *)&rp->v1, sizeof (struct rsdp_v1)) != 0)
1721 		return (0);
1722 
1723 	/* If pre-ACPI 2.0, this is a valid RSDP */
1724 	if (rp->v1.revision < 2)
1725 		return (1);
1726 
1727 	/* validate the V2.x checksum */
1728 	if (checksum_table((uint8_t *)rp, sizeof (struct rsdp)) != 0)
1729 		return (0);
1730 
1731 	return (1);
1732 }
1733 
1734 /*
1735  * Scan memory range for an RSDP;
1736  * see ACPI 3.0 Spec, 5.2.5.1
1737  */
1738 static struct rsdp *
1739 scan_rsdp(paddr_t start, paddr_t end)
1740 {
1741 	size_t len  = end - start + 1;
1742 	caddr_t ptr;
1743 
1744 	ptr = vmap_phys(len, start);
1745 	while (len > 0) {
1746 		if (strncmp(ptr, ACPI_RSDP_SIG, ACPI_RSDP_SIG_LEN) == 0)
1747 			if (valid_rsdp((struct rsdp *)ptr))
1748 				return ((struct rsdp *)ptr);
1749 		ptr += 16;
1750 		len -= 16;
1751 	}
1752 
1753 	return (NULL);
1754 }
1755 
1756 /*
1757  * Refer to ACPI 3.0 Spec, section 5.2.5.1 to understand this function
1758  */
1759 static struct rsdp *
1760 find_rsdp() {
1761 	struct rsdp *rsdp;
1762 	uint16_t *ebda_seg;
1763 	paddr_t  ebda_addr;
1764 
1765 	/*
1766 	 * Get the EBDA segment and scan the first 1K
1767 	 */
1768 	ebda_seg = (uint16_t *)vmap_phys(sizeof (uint16_t), ACPI_EBDA_SEG_ADDR);
1769 	ebda_addr = *ebda_seg << 4;
1770 	rsdp = scan_rsdp(ebda_addr, ebda_addr + ACPI_EBDA_LEN - 1);
1771 	if (rsdp == NULL)
1772 		/* if EBDA doesn't contain RSDP, look in BIOS memory */
1773 		rsdp = scan_rsdp(0xe0000, 0xfffff);
1774 	return (rsdp);
1775 }
1776 
1777 static struct table_header *
1778 map_fw_table(paddr_t table_addr)
1779 {
1780 	struct table_header *tp;
1781 	size_t len = MAX(sizeof (struct table_header), MMU_PAGESIZE);
1782 
1783 	/*
1784 	 * Map at least a page; if the table is larger than this, remap it
1785 	 */
1786 	tp = (struct table_header *)vmap_phys(len, table_addr);
1787 	if (tp->len > len)
1788 		tp = (struct table_header *)vmap_phys(tp->len, table_addr);
1789 	return (tp);
1790 }
1791 
1792 static struct table_header *
1793 find_fw_table(char *signature)
1794 {
1795 	static int revision = 0;
1796 	static struct xsdt *xsdt;
1797 	static int len;
1798 	paddr_t xsdt_addr;
1799 	struct rsdp *rsdp;
1800 	struct table_header *tp;
1801 	paddr_t table_addr;
1802 	int	n;
1803 
1804 	if (strlen(signature) != ACPI_TABLE_SIG_LEN)
1805 		return (NULL);
1806 
1807 	/*
1808 	 * Reading the ACPI 3.0 Spec, section 5.2.5.3 will help
1809 	 * understand this code.  If we haven't already found the RSDT/XSDT,
1810 	 * revision will be 0. Find the RSDP and check the revision
1811 	 * to find out whether to use the RSDT or XSDT.  If revision is
1812 	 * 0 or 1, use the RSDT and set internal revision to 1; if it is 2,
1813 	 * use the XSDT.  If the XSDT address is 0, though, fall back to
1814 	 * revision 1 and use the RSDT.
1815 	 */
1816 	if (revision == 0) {
1817 		if ((rsdp = (struct rsdp *)find_rsdp()) != NULL) {
1818 			revision = rsdp->v1.revision;
1819 			switch (revision) {
1820 			case 2:
1821 				/*
1822 				 * Use the XSDT unless BIOS is buggy and
1823 				 * claims to be rev 2 but has a null XSDT
1824 				 * address
1825 				 */
1826 				xsdt_addr = rsdp->xsdt;
1827 				if (xsdt_addr != 0)
1828 					break;
1829 				/* FALLTHROUGH */
1830 			case 0:
1831 				/* treat RSDP rev 0 as revision 1 internally */
1832 				revision = 1;
1833 				/* FALLTHROUGH */
1834 			case 1:
1835 				/* use the RSDT for rev 0/1 */
1836 				xsdt_addr = rsdp->v1.rsdt;
1837 				break;
1838 			default:
1839 				/* unknown revision */
1840 				revision = 0;
1841 				break;
1842 			}
1843 		}
1844 		if (revision == 0)
1845 			return (NULL);
1846 
1847 		/* cache the XSDT info */
1848 		xsdt = (struct xsdt *)map_fw_table(xsdt_addr);
1849 		len = (xsdt->hdr.len - sizeof (xsdt->hdr)) /
1850 		    ((revision == 1) ? sizeof (uint32_t) : sizeof (uint64_t));
1851 	}
1852 
1853 	/*
1854 	 * Scan the table headers looking for a signature match
1855 	 */
1856 	for (n = 0; n < len; n++) {
1857 		table_addr = (revision == 1) ? xsdt->p.r[n] : xsdt->p.x[n];
1858 		if (table_addr == 0)
1859 			continue;
1860 		tp = map_fw_table(table_addr);
1861 		if (strncmp(tp->sig, signature, ACPI_TABLE_SIG_LEN) == 0) {
1862 			return (tp);
1863 		}
1864 	}
1865 	return (NULL);
1866 }
1867 
1868 static void
1869 process_madt(struct madt *tp)
1870 {
1871 	struct madt_processor *cpu, *end;
1872 	uint32_t cpu_count = 0;
1873 	uint8_t cpu_apicid_array[UINT8_MAX + 1];
1874 
1875 	if (tp != NULL) {
1876 		/*
1877 		 * Determine number of CPUs and keep track of "final" APIC ID
1878 		 * for each CPU by walking through ACPI MADT processor list
1879 		 */
1880 		end = (struct madt_processor *)(tp->hdr.len + (uintptr_t)tp);
1881 		cpu = tp->list;
1882 		while (cpu < end) {
1883 			if (cpu->type == MADT_PROCESSOR) {
1884 				if (cpu->flags & 1) {
1885 					if (cpu_count < UINT8_MAX)
1886 						cpu_apicid_array[cpu_count] =
1887 						    cpu->apic_id;
1888 					cpu_count++;
1889 				}
1890 			}
1891 
1892 			cpu = (struct madt_processor *)
1893 			    (cpu->len + (uintptr_t)cpu);
1894 		}
1895 
1896 		/*
1897 		 * Make boot property for array of "final" APIC IDs for each
1898 		 * CPU
1899 		 */
1900 		bsetprop(BP_CPU_APICID_ARRAY, strlen(BP_CPU_APICID_ARRAY),
1901 		    cpu_apicid_array, cpu_count * sizeof (uint8_t));
1902 	}
1903 
1904 	/*
1905 	 * User-set boot-ncpus overrides firmware count
1906 	 */
1907 	if (do_bsys_getproplen(NULL, "boot-ncpus") >= 0)
1908 		return;
1909 
1910 	/*
1911 	 * Set boot property for boot-ncpus to number of CPUs given in MADT
1912 	 * if user hasn't set the property already
1913 	 */
1914 	if (tp != NULL)
1915 		bsetpropsi("boot-ncpus", cpu_count);
1916 }
1917 
1918 static void
1919 process_srat(struct srat *tp)
1920 {
1921 	struct srat_item *item, *end;
1922 	int i;
1923 	int proc_num, mem_num;
1924 #pragma pack(1)
1925 	struct {
1926 		uint32_t domain;
1927 		uint32_t apic_id;
1928 		uint32_t sapic_id;
1929 	} processor;
1930 	struct {
1931 		uint32_t domain;
1932 		uint32_t x2apic_id;
1933 	} x2apic;
1934 	struct {
1935 		uint32_t domain;
1936 		uint64_t addr;
1937 		uint64_t length;
1938 		uint32_t flags;
1939 	} memory;
1940 #pragma pack()
1941 	char prop_name[30];
1942 
1943 	if (tp == NULL)
1944 		return;
1945 
1946 	proc_num = mem_num = 0;
1947 	end = (struct srat_item *)(tp->hdr.len + (uintptr_t)tp);
1948 	item = tp->list;
1949 	while (item < end) {
1950 		switch (item->type) {
1951 		case SRAT_PROCESSOR:
1952 			if (!(item->i.p.flags & SRAT_ENABLED))
1953 				break;
1954 			processor.domain = item->i.p.domain1;
1955 			for (i = 0; i < 3; i++)
1956 				processor.domain +=
1957 				    item->i.p.domain2[i] << ((i + 1) * 8);
1958 			processor.apic_id = item->i.p.apic_id;
1959 			processor.sapic_id = item->i.p.local_sapic_eid;
1960 			(void) snprintf(prop_name, 30, "acpi-srat-processor-%d",
1961 			    proc_num);
1962 			bsetprop(prop_name, strlen(prop_name), &processor,
1963 			    sizeof (processor));
1964 			proc_num++;
1965 			break;
1966 		case SRAT_MEMORY:
1967 			if (!(item->i.m.flags & SRAT_ENABLED))
1968 				break;
1969 			memory.domain = item->i.m.domain;
1970 			memory.addr = item->i.m.base_addr;
1971 			memory.length = item->i.m.len;
1972 			memory.flags = item->i.m.flags;
1973 			(void) snprintf(prop_name, 30, "acpi-srat-memory-%d",
1974 			    mem_num);
1975 			bsetprop(prop_name, strlen(prop_name), &memory,
1976 			    sizeof (memory));
1977 			mem_num++;
1978 			break;
1979 		case SRAT_X2APIC:
1980 			if (!(item->i.xp.flags & SRAT_ENABLED))
1981 				break;
1982 			x2apic.domain = item->i.xp.domain;
1983 			x2apic.x2apic_id = item->i.xp.x2apic_id;
1984 			(void) snprintf(prop_name, 30, "acpi-srat-processor-%d",
1985 			    proc_num);
1986 			bsetprop(prop_name, strlen(prop_name), &x2apic,
1987 			    sizeof (x2apic));
1988 			proc_num++;
1989 			break;
1990 		}
1991 
1992 		item = (struct srat_item *)
1993 		    (item->len + (caddr_t)item);
1994 	}
1995 }
1996 
1997 static void
1998 process_slit(struct slit *tp)
1999 {
2000 
2001 	/*
2002 	 * Check the number of localities; if it's too huge, we just
2003 	 * return and locality enumeration code will handle this later,
2004 	 * if possible.
2005 	 *
2006 	 * Note that the size of the table is the square of the
2007 	 * number of localities; if the number of localities exceeds
2008 	 * UINT16_MAX, the table size may overflow an int when being
2009 	 * passed to bsetprop() below.
2010 	 */
2011 	if (tp->number >= SLIT_LOCALITIES_MAX)
2012 		return;
2013 
2014 	bsetprop(SLIT_NUM_PROPNAME, strlen(SLIT_NUM_PROPNAME), &tp->number,
2015 	    sizeof (tp->number));
2016 	bsetprop(SLIT_PROPNAME, strlen(SLIT_PROPNAME), &tp->entry,
2017 	    tp->number * tp->number);
2018 }
2019 
2020 static void
2021 process_dmar(struct dmar *tp)
2022 {
2023 	bsetprop(DMAR_TABLE_PROPNAME, strlen(DMAR_TABLE_PROPNAME),
2024 	    tp, tp->hdr.len);
2025 }
2026 
2027 #else /* __xpv */
2028 static void
2029 enumerate_xen_cpus()
2030 {
2031 	processorid_t	id, max_id;
2032 
2033 	/*
2034 	 * User-set boot-ncpus overrides enumeration
2035 	 */
2036 	if (do_bsys_getproplen(NULL, "boot-ncpus") >= 0)
2037 		return;
2038 
2039 	/*
2040 	 * Probe every possible virtual CPU id and remember the
2041 	 * highest id present; the count of CPUs is one greater
2042 	 * than this.  This tacitly assumes at least cpu 0 is present.
2043 	 */
2044 	max_id = 0;
2045 	for (id = 0; id < MAX_VIRT_CPUS; id++)
2046 		if (HYPERVISOR_vcpu_op(VCPUOP_is_up, id, NULL) == 0)
2047 			max_id = id;
2048 
2049 	bsetpropsi("boot-ncpus", max_id+1);
2050 
2051 }
2052 #endif /* __xpv */
2053 
2054 static void
2055 build_firmware_properties(void)
2056 {
2057 #ifndef __xpv
2058 	struct table_header *tp;
2059 
2060 	if ((tp = find_fw_table("APIC")) != NULL)
2061 		process_madt((struct madt *)tp);
2062 
2063 	if ((srat_ptr = (struct srat *)find_fw_table("SRAT")) != NULL)
2064 		process_srat(srat_ptr);
2065 
2066 	if (slit_ptr = (struct slit *)find_fw_table("SLIT"))
2067 		process_slit(slit_ptr);
2068 
2069 	if (tp = find_fw_table("DMAR"))
2070 		process_dmar((struct dmar *)tp);
2071 #else /* __xpv */
2072 	enumerate_xen_cpus();
2073 #endif /* __xpv */
2074 }
2075 
2076 /*
2077  * fake up a boot property for USB serial console early boot output
2078  */
2079 void *
2080 usbser_init(size_t size)
2081 {
2082 	static char *p = NULL;
2083 
2084 	p = do_bsys_alloc(NULL, NULL, size, MMU_PAGESIZE);
2085 	*p = 0;
2086 	bsetprop("usb-serial-buf", strlen("usb-serial-buf") + 1,
2087 	    &p, sizeof (p));
2088 	return (p);
2089 }
2090 
2091 /*ARGSUSED*/
2092 int
2093 boot_compinfo(int fd, struct compinfo *cbp)
2094 {
2095 	cbp->iscmp = 0;
2096 	cbp->blksize = MAXBSIZE;
2097 	return (0);
2098 }
2099