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);
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
ofwtramp(void * buf,ofwfp_t cb)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
call_ofw(ofwcell_t * buf)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
ofw_init(void * vpd,int res,ofwfp_t openfirm,char * arg,int argl)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
ofw_finddevice(const char * name)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
ofw_getprop(ofwh_t ofwh,const char * name,void * buf,size_t len)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
ofw_setprop(ofwh_t ofwh,const char * name,void * buf,size_t len)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
ofw_open(const char * path)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
ofw_close(ofwh_t devh)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
ofw_claim(void * virt,size_t len,u_int align)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
ofw_read(ofwh_t devh,void * buf,size_t len)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
ofw_write(ofwh_t devh,const void * buf,size_t len)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
ofw_seek(ofwh_t devh,uint64_t off)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
ofw_exit(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
bcopy(const void * src,void * dst,size_t len)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
memcpy(void * dst,const void * src,size_t len)457 memcpy(void *dst, const void *src, size_t len)
458 {
459 bcopy(src, dst, len);
460 }
461
462 static void
bzero(void * b,size_t len)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
strcmp(const char * s1,const char * s2)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
main(int ac,char ** av)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
usage(void)553 usage(void)
554 {
555
556 printf("usage: boot device [/path/to/loader]\n");
557 exit(1);
558 }
559
560 static void
exit(int code)561 exit(int code)
562 {
563
564 ofw_exit();
565 }
566
567 static struct dmadat __dmadat;
568
569 static int
domount(const char * device,int quiet)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
load(const char * fname)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
dskread(void * buf,uint64_t lba,int nblk)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
panic(const char * fmt,...)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
printf(const char * fmt,...)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
putchar(char c,void * arg)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
vprintf(const char * fmt,va_list ap)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
vsnprintf(char * str,size_t sz,const char * fmt,va_list ap)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
__printf(const char * fmt,putc_func_t * putc,void * arg,va_list ap)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
__sputc(char c,void * arg)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
__puts(const char * s,putc_func_t * putc,void * arg)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 *
__uitoa(char * buf,u_int ui,int base)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 *
__ultoa(char * buf,u_long ul,int base)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