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