xref: /freebsd/stand/powerpc/boot1.chrp/boot1.c (revision e453e498cbb88570a3ff7b3679de65c88707da95)
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