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