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