xref: /freebsd/stand/powerpc/boot1.chrp/boot1.c (revision 315ee00f)
1 /*-
2  * Copyright (c) 1998 Robert Nordier
3  * All rights reserved.
4  * Copyright (c) 2001 Robert Drehmel
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms are freely
8  * permitted provided that the above copyright notice and this
9  * paragraph and the following disclaimer are duplicated in all
10  * such forms.
11  *
12  * This software is provided "AS IS" and without any express or
13  * implied warranties, including, without limitation, the implied
14  * warranties of merchantability and fitness for a particular
15  * purpose.
16  */
17 
18 #include <sys/cdefs.h>
19 #include <sys/param.h>
20 #include <sys/dirent.h>
21 #include <sys/endian.h>
22 #include <machine/elf.h>
23 #include <machine/stdarg.h>
24 #include <machine/md_var.h>
25 #include <ufs/ffs/fs.h>
26 
27 #include "paths.h"
28 
29 #define BSIZEMAX	16384
30 
31 typedef int putc_func_t(char c, void *arg);
32 typedef int32_t ofwh_t;
33 
34 struct sp_data {
35 	char	*sp_buf;
36 	u_int	sp_len;
37 	u_int	sp_size;
38 };
39 
40 static const char digits[] = "0123456789abcdef";
41 
42 static char bootpath[128];
43 static char bootargs[128];
44 
45 static ofwh_t bootdev;
46 
47 static struct fs fs;
48 static char blkbuf[BSIZEMAX];
49 static unsigned int fsblks;
50 
51 static uint32_t fs_off;
52 
53 int main(int ac, char **av);
54 
55 static void exit(int) __dead2;
56 static void load(const char *);
57 static int dskread(void *, uint64_t, int);
58 
59 static void usage(void) __dead2;
60 
61 static void bcopy(const void *src, void *dst, size_t len);
62 static void bzero(void *b, size_t len);
63 
64 static int domount(const char *device, int quiet);
65 
66 static void panic(const char *fmt, ...) __dead2;
67 static int printf(const char *fmt, ...);
68 static int putchar(char c, void *arg);
69 static int vprintf(const char *fmt, va_list ap);
70 static int vsnprintf(char *str, size_t sz, const char *fmt, va_list ap);
71 
72 static int __printf(const char *fmt, putc_func_t *putc, void *arg, va_list ap);
73 static int __putc(char c, void *arg);
74 static int __puts(const char *s, putc_func_t *putc, void *arg);
75 static int __sputc(char c, void *arg);
76 static char *__uitoa(char *buf, u_int val, int base);
77 static char *__ultoa(char *buf, u_long val, int base);
78 
79 /*
80  * Open Firmware interface functions
81  */
82 typedef uint32_t	ofwcell_t;
83 typedef uint32_t	u_ofwh_t;
84 typedef int (*ofwfp_t)(ofwcell_t *);
85 ofwfp_t ofw;			/* the prom Open Firmware entry */
86 ofwh_t chosenh;
87 
88 void ofw_init(void *, int, ofwfp_t, char *, int);
89 static ofwh_t ofw_finddevice(const char *);
90 static ofwh_t ofw_open(const char *);
91 static int ofw_close(ofwh_t);
92 static int ofw_getprop(ofwh_t, const char *, void *, size_t);
93 static int ofw_setprop(ofwh_t, const char *, void *, size_t);
94 static int ofw_read(ofwh_t, void *, size_t);
95 static int ofw_write(ofwh_t, const void *, size_t);
96 static int ofw_claim(void *virt, size_t len, u_int align);
97 static int ofw_seek(ofwh_t, uint64_t);
98 static void ofw_exit(void) __dead2;
99 
100 ofwh_t bootdevh;
101 ofwh_t stdinh, stdouth;
102 
103 /*
104  * Note about the entry point:
105  *
106  * For some odd reason, the first page of the load appears to have trouble
107  * when entering in LE. The first five instructions decode weirdly.
108  * I suspect it is some cache weirdness between the ELF headers and .text.
109  *
110  * Ensure we have a gap between the start of .text and the entry as a
111  * workaround.
112  */
113 __asm("                         \n\
114         .data                   \n\
115 	.align 4		\n\
116 stack:                          \n\
117         .space  16384           \n\
118                                 \n\
119         .text                   \n\
120         /* SLOF cache hack */   \n\
121         .space 4096             \n\
122         .globl  _start          \n\
123 _start:                         \n\
124         lis     %r1,stack@ha    \n\
125         addi    %r1,%r1,stack@l \n\
126         addi    %r1,%r1,8192    \n\
127                                 \n\
128         b       ofw_init        \n\
129 ");
130 
131 ofwfp_t realofw;
132 
133 #if BYTE_ORDER == LITTLE_ENDIAN
134 /*
135  * Minimal endianness-swap trampoline for LE.
136  */
137 __attribute__((naked)) int
138 ofwtramp(void *buf, ofwfp_t cb)
139 {
140 __asm("									\n\
141 	mflr	%r0							\n\
142 	stw	%r0, 4(%r1)						\n\
143 	stwu	%r1, -16(%r1)						\n\
144 	stw	%r30, 8(%r1)						\n\
145 	/* Save current MSR for restoration post-call. */		\n\
146 	mfmsr	%r30							\n\
147 	mr	%r5, %r30						\n\
148 	/* Remove LE bit from MSR. */					\n\
149 	clrrwi	%r5, %r5, 1						\n\
150 	mtsrr0	%r4							\n\
151 	mtsrr1	%r5							\n\
152 	bcl	20, 31, .+4	/* LOAD_LR_NIA */			\n\
153 1:									\n\
154 	mflr	%r4							\n\
155 	addi	%r4, %r4, (2f - 1b)					\n\
156 	mtlr	%r4							\n\
157 	/* Switch to BE and transfer control to OF entry */		\n\
158 	rfid								\n\
159 2:									\n\
160 	/* Control is returned here, but in BE. */			\n\
161 	.long	0x05009f42	/* LOAD_LR_NIA			      */\n\
162 				/* 0:			 	      */\n\
163 	.long	0xa603db7f	/* mtsrr1 	%r30		      */\n\
164 	.long	0xa602c87f	/* mflr		%r30		      */\n\
165 	.long	0x1400de3b	/* addi		%r30, %r30, (1f - 0b) */\n\
166 	.long	0xa603da7f	/* mtsrr0	%r30		      */\n\
167 	.long	0x2400004c	/* rfid				      */\n\
168 				/* 1:				      */\n\
169 1:									\n\
170 	/* Back to normal. Tidy up for return. */			\n\
171 	lwz	%r30, 8(%r1)						\n\
172 	lwz	%r0, 20(%r1)						\n\
173 	addi	%r1, %r1, 16						\n\
174 	mtlr	%r0							\n\
175 	blr								\n\
176 ");
177 }
178 
179 /*
180  * Little-endian OFW entrypoint replacement.
181  *
182  * We are doing all the byteswapping in one place here to save space.
183  * This means instance handles will be byteswapped as well.
184  */
185 int
186 call_ofw(ofwcell_t* buf)
187 {
188 	int ret, i, ncells;
189 
190 	ncells = 3 + buf[1] + buf[2];
191 	for (i = 0; i < ncells; i++)
192 		buf[i] = htobe32(buf[i]);
193 
194 	ret = (ofwtramp(buf, realofw));
195 	for (i = 0; i < ncells; i++)
196 		buf[i] = be32toh(buf[i]);
197 	return (ret);
198 }
199 #endif
200 
201 void
202 ofw_init(void *vpd, int res, ofwfp_t openfirm, char *arg, int argl)
203 {
204 	char *av[16];
205 	char *p;
206 	int ac;
207 
208 #if BYTE_ORDER == LITTLE_ENDIAN
209 	realofw = openfirm;
210 	ofw = call_ofw;
211 #else
212 	realofw = ofw = openfirm;
213 #endif
214 
215 	chosenh = ofw_finddevice("/chosen");
216 	ofw_getprop(chosenh, "stdin", &stdinh, sizeof(stdinh));
217 	stdinh = be32toh(stdinh);
218 	ofw_getprop(chosenh, "stdout", &stdouth, sizeof(stdouth));
219 	stdouth = be32toh(stdouth);
220 	ofw_getprop(chosenh, "bootargs", bootargs, sizeof(bootargs));
221 	ofw_getprop(chosenh, "bootpath", bootpath, sizeof(bootpath));
222 
223 	bootargs[sizeof(bootargs) - 1] = '\0';
224 	bootpath[sizeof(bootpath) - 1] = '\0';
225 
226 	p = bootpath;
227 	while (*p != '\0') {
228 		/* Truncate partition ID */
229 		if (*p == ':') {
230 			ofw_close(bootdev);
231 			*(++p) = '\0';
232 			break;
233 		}
234 		p++;
235 	}
236 
237 	ac = 0;
238 	p = bootargs;
239 	for (;;) {
240 		while (*p == ' ' && *p != '\0')
241 			p++;
242 		if (*p == '\0' || ac >= 16)
243 			break;
244 		av[ac++] = p;
245 		while (*p != ' ' && *p != '\0')
246 			p++;
247 		if (*p != '\0')
248 			*p++ = '\0';
249 	}
250 
251 	exit(main(ac, av));
252 }
253 
254 static ofwh_t
255 ofw_finddevice(const char *name)
256 {
257 	ofwcell_t args[] = {
258 		(ofwcell_t)"finddevice",
259 		1,
260 		1,
261 		(ofwcell_t)name,
262 		0
263 	};
264 
265 	if ((*ofw)(args)) {
266 		printf("ofw_finddevice: name=\"%s\"\n", name);
267 		return (1);
268 	}
269 	return (args[4]);
270 }
271 
272 static int
273 ofw_getprop(ofwh_t ofwh, const char *name, void *buf, size_t len)
274 {
275 	ofwcell_t args[] = {
276 		(ofwcell_t)"getprop",
277 		4,
278 		1,
279 		(u_ofwh_t)ofwh,
280 		(ofwcell_t)name,
281 		(ofwcell_t)buf,
282 		len,
283 	0
284 	};
285 
286 	if ((*ofw)(args)) {
287 		printf("ofw_getprop: ofwh=0x%x buf=%p len=%u\n",
288 			ofwh, buf, len);
289 		return (1);
290 	}
291 	return (0);
292 }
293 
294 static int
295 ofw_setprop(ofwh_t ofwh, const char *name, void *buf, size_t len)
296 {
297 	ofwcell_t args[] = {
298 		(ofwcell_t)"setprop",
299 		4,
300 		1,
301 		(u_ofwh_t)ofwh,
302 		(ofwcell_t)name,
303 		(ofwcell_t)buf,
304 		len,
305 	0
306 	};
307 
308 	if ((*ofw)(args)) {
309 		printf("ofw_setprop: ofwh=0x%x buf=%p len=%u\n",
310 			ofwh, buf, len);
311 		return (1);
312 	}
313 	return (0);
314 }
315 
316 static ofwh_t
317 ofw_open(const char *path)
318 {
319 	ofwcell_t args[] = {
320 		(ofwcell_t)"open",
321 		1,
322 		1,
323 		(ofwcell_t)path,
324 		0
325 	};
326 
327 	if ((*ofw)(args)) {
328 		printf("ofw_open: path=\"%s\"\n", path);
329 		return (-1);
330 	}
331 	return (args[4]);
332 }
333 
334 static int
335 ofw_close(ofwh_t devh)
336 {
337 	ofwcell_t args[] = {
338 		(ofwcell_t)"close",
339 		1,
340 		0,
341 		(u_ofwh_t)devh
342 	};
343 
344 	if ((*ofw)(args)) {
345 		printf("ofw_close: devh=0x%x\n", devh);
346 		return (1);
347 	}
348 	return (0);
349 }
350 
351 static int
352 ofw_claim(void *virt, size_t len, u_int align)
353 {
354 	ofwcell_t args[] = {
355 		(ofwcell_t)"claim",
356 		3,
357 		1,
358 		(ofwcell_t)virt,
359 		len,
360 		align,
361 		0,
362 		0
363 	};
364 
365 	if ((*ofw)(args)) {
366 		printf("ofw_claim: virt=%p len=%u\n", virt, len);
367 		return (1);
368 	}
369 
370 	return (0);
371 }
372 
373 static int
374 ofw_read(ofwh_t devh, void *buf, size_t len)
375 {
376 	ofwcell_t args[] = {
377 		(ofwcell_t)"read",
378 		3,
379 		1,
380 		(u_ofwh_t)devh,
381 		(ofwcell_t)buf,
382 		len,
383 		0
384 	};
385 
386 	if ((*ofw)(args)) {
387 		printf("ofw_read: devh=0x%x buf=%p len=%u\n", devh, buf, len);
388 		return (1);
389 	}
390 	return (0);
391 }
392 
393 static int
394 ofw_write(ofwh_t devh, const void *buf, size_t len)
395 {
396 	ofwcell_t args[] = {
397 		(ofwcell_t)"write",
398 		3,
399 		1,
400 		(u_ofwh_t)devh,
401 		(ofwcell_t)buf,
402 		len,
403 		0
404 	};
405 
406 	if ((*ofw)(args)) {
407 		printf("ofw_write: devh=0x%x buf=%p len=%u\n", devh, buf, len);
408 		return (1);
409 	}
410 	return (0);
411 }
412 
413 static int
414 ofw_seek(ofwh_t devh, uint64_t off)
415 {
416 	ofwcell_t args[] = {
417 		(ofwcell_t)"seek",
418 		3,
419 		1,
420 		(u_ofwh_t)devh,
421 		off >> 32,
422 		off,
423 		0
424 	};
425 
426 	if ((*ofw)(args)) {
427 		printf("ofw_seek: devh=0x%x off=0x%lx\n", devh, off);
428 		return (1);
429 	}
430 	return (0);
431 }
432 
433 static void
434 ofw_exit(void)
435 {
436 	ofwcell_t args[3];
437 
438 	args[0] = (ofwcell_t)"exit";
439 	args[1] = 0;
440 	args[2] = 0;
441 
442 	for (;;)
443 		(*ofw)(args);
444 }
445 
446 static void
447 bcopy(const void *src, void *dst, size_t len)
448 {
449 	const char *s = src;
450 	char *d = dst;
451 
452 	while (len-- != 0)
453 		*d++ = *s++;
454 }
455 
456 static void
457 memcpy(void *dst, const void *src, size_t len)
458 {
459 	bcopy(src, dst, len);
460 }
461 
462 static void
463 bzero(void *b, size_t len)
464 {
465 	char *p = b;
466 
467 	while (len-- != 0)
468 		*p++ = 0;
469 }
470 
471 static int
472 strcmp(const char *s1, const char *s2)
473 {
474 	for (; *s1 == *s2 && *s1; s1++, s2++)
475 		;
476 	return ((u_char)*s1 - (u_char)*s2);
477 }
478 
479 #include "ufsread.c"
480 
481 int
482 main(int ac, char **av)
483 {
484 	const char *path;
485 	char bootpath_full[255];
486 	int i, len;
487 
488 	path = PATH_LOADER;
489 	for (i = 0; i < ac; i++) {
490 		switch (av[i][0]) {
491 		case '-':
492 			switch (av[i][1]) {
493 			default:
494 				usage();
495 			}
496 			break;
497 		default:
498 			path = av[i];
499 			break;
500 		}
501 	}
502 
503 	printf(" \n>> FreeBSD/powerpc Open Firmware boot block\n"
504 	"   Boot path:   %s\n"
505 	"   Boot loader: %s\n", bootpath, path);
506 
507 	len = 0;
508 	while (bootpath[len] != '\0') len++;
509 
510 	memcpy(bootpath_full,bootpath,len+1);
511 
512 	if (bootpath_full[len-1] != ':') {
513 		/* First try full volume */
514 		if (domount(bootpath_full,1) == 0)
515 			goto out;
516 
517 		/* Add a : so that we try partitions if that fails */
518 		if (bootdev > 0)
519 			ofw_close(bootdev);
520 		bootpath_full[len] = ':';
521 		len += 1;
522 	}
523 
524 	/* Loop through first 16 partitions to find a UFS one */
525 	for (i = 0; i < 16; i++) {
526 		if (i < 10) {
527 			bootpath_full[len] = i + '0';
528 			bootpath_full[len+1] = '\0';
529 		} else {
530 			bootpath_full[len] = '1';
531 			bootpath_full[len+1] = i - 10 + '0';
532 			bootpath_full[len+2] = '\0';
533 		}
534 
535 		if (domount(bootpath_full,1) >= 0)
536 			break;
537 
538 		if (bootdev > 0)
539 			ofw_close(bootdev);
540 	}
541 
542 	if (i >= 16)
543 		panic("domount");
544 
545 out:
546 	printf("   Boot volume:   %s\n",bootpath_full);
547 	ofw_setprop(chosenh, "bootargs", bootpath_full, len+2);
548 	load(path);
549 	return (1);
550 }
551 
552 static void
553 usage(void)
554 {
555 
556 	printf("usage: boot device [/path/to/loader]\n");
557 	exit(1);
558 }
559 
560 static void
561 exit(int code)
562 {
563 
564 	ofw_exit();
565 }
566 
567 static struct dmadat __dmadat;
568 
569 static int
570 domount(const char *device, int quiet)
571 {
572 
573 	dmadat = &__dmadat;
574 	if ((bootdev = ofw_open(device)) == -1) {
575 		printf("domount: can't open device\n");
576 		return (-1);
577 	}
578 	if (fsread(0, NULL, 0)) {
579 		if (!quiet)
580 			printf("domount: can't read superblock\n");
581 		return (-1);
582 	}
583 	return (0);
584 }
585 
586 static void
587 load(const char *fname)
588 {
589 	Elf32_Ehdr eh;
590 	Elf32_Phdr ph;
591 	caddr_t p;
592 	ufs_ino_t ino;
593 	int i;
594 
595 	if ((ino = lookup(fname)) == 0) {
596 		printf("File %s not found\n", fname);
597 		return;
598 	}
599 	if (fsread(ino, &eh, sizeof(eh)) != sizeof(eh)) {
600 		printf("Can't read elf header\n");
601 		return;
602 	}
603 	if (!IS_ELF(eh)) {
604 		printf("Not an ELF file\n");
605 		return;
606 	}
607 	for (i = 0; i < eh.e_phnum; i++) {
608 		fs_off = eh.e_phoff + i * eh.e_phentsize;
609 		if (fsread(ino, &ph, sizeof(ph)) != sizeof(ph)) {
610 			printf("Can't read program header %d\n", i);
611 			return;
612 		}
613 		if (ph.p_type != PT_LOAD)
614 			continue;
615 		fs_off = ph.p_offset;
616 		p = (caddr_t)ph.p_vaddr;
617 		ofw_claim(p,(ph.p_filesz > ph.p_memsz) ?
618 		    ph.p_filesz : ph.p_memsz,0);
619 		if (fsread(ino, p, ph.p_filesz) != ph.p_filesz) {
620 			printf("Can't read content of section %d\n", i);
621 			return;
622 		}
623 		if (ph.p_filesz != ph.p_memsz)
624 			bzero(p + ph.p_filesz, ph.p_memsz - ph.p_filesz);
625 		__syncicache(p, ph.p_memsz);
626 	}
627 	ofw_close(bootdev);
628 	(*(void (*)(void *, int, ofwfp_t, char *, int))eh.e_entry)(NULL, 0,
629 	    realofw, NULL, 0);
630 }
631 
632 static int
633 dskread(void *buf, uint64_t lba, int nblk)
634 {
635 	/*
636 	 * The Open Firmware should open the correct partition for us.
637 	 * That means, if we read from offset zero on an open instance handle,
638 	 * we should read from offset zero of that partition.
639 	 */
640 	ofw_seek(bootdev, lba * DEV_BSIZE);
641 	ofw_read(bootdev, buf, nblk * DEV_BSIZE);
642 	return (0);
643 }
644 
645 static void
646 panic(const char *fmt, ...)
647 {
648 	char buf[128];
649 	va_list ap;
650 
651 	va_start(ap, fmt);
652 	vsnprintf(buf, sizeof buf, fmt, ap);
653 	printf("panic: %s\n", buf);
654 	va_end(ap);
655 
656 	exit(1);
657 }
658 
659 static int
660 printf(const char *fmt, ...)
661 {
662 	va_list ap;
663 	int ret;
664 
665 	va_start(ap, fmt);
666 	ret = vprintf(fmt, ap);
667 	va_end(ap);
668 	return (ret);
669 }
670 
671 static int
672 putchar(char c, void *arg)
673 {
674 	char buf;
675 
676 	if (c == '\n') {
677 		buf = '\r';
678 		ofw_write(stdouth, &buf, 1);
679 	}
680 	buf = c;
681 	ofw_write(stdouth, &buf, 1);
682 	return (1);
683 }
684 
685 static int
686 vprintf(const char *fmt, va_list ap)
687 {
688 	int ret;
689 
690 	ret = __printf(fmt, putchar, 0, ap);
691 	return (ret);
692 }
693 
694 static int
695 vsnprintf(char *str, size_t sz, const char *fmt, va_list ap)
696 {
697 	struct sp_data sp;
698 	int ret;
699 
700 	sp.sp_buf = str;
701 	sp.sp_len = 0;
702 	sp.sp_size = sz;
703 	ret = __printf(fmt, __sputc, &sp, ap);
704 	return (ret);
705 }
706 
707 static int
708 __printf(const char *fmt, putc_func_t *putc, void *arg, va_list ap)
709 {
710 	char buf[(sizeof(long) * 8) + 1];
711 	char *nbuf;
712 	u_long ul;
713 	u_int ui;
714 	int lflag;
715 	int sflag;
716 	char *s;
717 	int pad;
718 	int ret;
719 	int c;
720 
721 	nbuf = &buf[sizeof buf - 1];
722 	ret = 0;
723 	while ((c = *fmt++) != 0) {
724 		if (c != '%') {
725 			ret += putc(c, arg);
726 			continue;
727 		}
728 		lflag = 0;
729 		sflag = 0;
730 		pad = 0;
731 reswitch:	c = *fmt++;
732 		switch (c) {
733 		case '#':
734 			sflag = 1;
735 			goto reswitch;
736 		case '%':
737 			ret += putc('%', arg);
738 			break;
739 		case 'c':
740 			c = va_arg(ap, int);
741 			ret += putc(c, arg);
742 			break;
743 		case 'd':
744 			if (lflag == 0) {
745 				ui = (u_int)va_arg(ap, int);
746 				if (ui < (int)ui) {
747 					ui = -ui;
748 					ret += putc('-', arg);
749 				}
750 				s = __uitoa(nbuf, ui, 10);
751 			} else {
752 				ul = (u_long)va_arg(ap, long);
753 				if (ul < (long)ul) {
754 					ul = -ul;
755 					ret += putc('-', arg);
756 				}
757 				s = __ultoa(nbuf, ul, 10);
758 			}
759 			ret += __puts(s, putc, arg);
760 			break;
761 		case 'l':
762 			lflag = 1;
763 			goto reswitch;
764 		case 'o':
765 			if (lflag == 0) {
766 				ui = (u_int)va_arg(ap, u_int);
767 				s = __uitoa(nbuf, ui, 8);
768 			} else {
769 				ul = (u_long)va_arg(ap, u_long);
770 				s = __ultoa(nbuf, ul, 8);
771 			}
772 			ret += __puts(s, putc, arg);
773 			break;
774 		case 'p':
775 			ul = (u_long)va_arg(ap, void *);
776 			s = __ultoa(nbuf, ul, 16);
777 			ret += __puts("0x", putc, arg);
778 			ret += __puts(s, putc, arg);
779 			break;
780 		case 's':
781 			s = va_arg(ap, char *);
782 			ret += __puts(s, putc, arg);
783 			break;
784 		case 'u':
785 			if (lflag == 0) {
786 				ui = va_arg(ap, u_int);
787 				s = __uitoa(nbuf, ui, 10);
788 			} else {
789 				ul = va_arg(ap, u_long);
790 				s = __ultoa(nbuf, ul, 10);
791 			}
792 			ret += __puts(s, putc, arg);
793 			break;
794 		case 'x':
795 			if (lflag == 0) {
796 				ui = va_arg(ap, u_int);
797 				s = __uitoa(nbuf, ui, 16);
798 			} else {
799 				ul = va_arg(ap, u_long);
800 				s = __ultoa(nbuf, ul, 16);
801 			}
802 			if (sflag)
803 				ret += __puts("0x", putc, arg);
804 			ret += __puts(s, putc, arg);
805 			break;
806 		case '0': case '1': case '2': case '3': case '4':
807 		case '5': case '6': case '7': case '8': case '9':
808 			pad = pad * 10 + c - '0';
809 			goto reswitch;
810 		default:
811 			break;
812 		}
813 	}
814 	return (ret);
815 }
816 
817 static int
818 __sputc(char c, void *arg)
819 {
820 	struct sp_data *sp;
821 
822 	sp = arg;
823 	if (sp->sp_len < sp->sp_size)
824 		sp->sp_buf[sp->sp_len++] = c;
825 	sp->sp_buf[sp->sp_len] = '\0';
826 	return (1);
827 }
828 
829 static int
830 __puts(const char *s, putc_func_t *putc, void *arg)
831 {
832 	const char *p;
833 	int ret;
834 
835 	ret = 0;
836 	for (p = s; *p != '\0'; p++)
837 		ret += putc(*p, arg);
838 	return (ret);
839 }
840 
841 static char *
842 __uitoa(char *buf, u_int ui, int base)
843 {
844 	char *p;
845 
846 	p = buf;
847 	*p = '\0';
848 	do
849 		*--p = digits[ui % base];
850 	while ((ui /= base) != 0);
851 	return (p);
852 }
853 
854 static char *
855 __ultoa(char *buf, u_long ul, int base)
856 {
857 	char *p;
858 
859 	p = buf;
860 	*p = '\0';
861 	do
862 		*--p = digits[ul % base];
863 	while ((ul /= base) != 0);
864 	return (p);
865 }
866