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