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