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