xref: /netbsd-src/sys/arch/amiga/stand/loadbsd/loadbsd.c (revision 6ea46cb5e46c49111a6ecf3bcbe3c7e2730fe9f6)
1 /*
2  * Copyright (c) 1994 Michael L. Hitch
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Michael L. Hitch.
16  * 4. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  *	$Id: loadbsd.c,v 1.14 1994/06/29 13:13:11 chopps Exp $
31  */
32 
33 #include <sys/types.h>
34 #include <a.out.h>
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <stdarg.h>
39 #include <signal.h>
40 #ifdef __NetBSD__
41 #include <err.h>
42 #endif
43 #include <exec/types.h>
44 #include <exec/execbase.h>
45 #include <exec/memory.h>
46 #include <exec/resident.h>
47 #include <graphics/gfxbase.h>
48 #include <libraries/configregs.h>
49 #include <libraries/configvars.h>
50 #include <libraries/expansion.h>
51 #include <libraries/expansionbase.h>
52 
53 #include <inline/exec.h>
54 #include <inline/expansion.h>
55 #include <inline/graphics.h>
56 
57 /* Get definitions for boothowto */
58 #include "reboot.h"
59 
60 #undef __LDPGSZ
61 #define __LDPGSZ 8192
62 
63 #ifndef __NetBSD__
64 #ifndef __P
65 #ifdef __STDC__
66 #define __P(x) x
67 #else
68 #define __P(x)
69 #endif
70 #endif
71 void err __P((int, const char *, ...));
72 void errx __P((int, const char *, ...));
73 void warn __P((const char *, ...));
74 void warnx __P((const char *, ...));
75 #endif
76 
77 /*
78  *	Version history:
79  *	1.x	Kernel parameter passing version check.
80  *	2.0	Added symbol table end address and symbol table support.
81  *	2.1	03/23/94 - Round up end of fastram segment.
82  *		Check fastram segment size for minimum of 2M.
83  *		Use largest segment of highest priority if -p option.
84  *		Print out fastram size in KB if not a multiple of MB.
85  *	2.2	03/24/94 - Zero out all unused registers.
86  *		Started version history comment.
87  *	2.3	04/26/94 - Added -D option to enter debugger on boot.
88  *	2.4	04/30/94 - Cpuid includes base machine type.
89  *		Also check if CPU is capable of running NetBSD.
90  *	2.5	05/17/94 - Add check for "A3000 bonus".
91  *	2.6	06/05/94 - Added -c option to override machine type.
92  *	2.7	06/15/94 - Pass E clock frequency.
93  *	2.8	06/22/94 - Fix supervisor stack usage.
94  *	2.9	06/26/94 - Use PAL flag for E clock freq on pre 2.0 WB
95  *		Added AGA enable parameter
96  */
97 static const char _version[] = "$VER: LoadBSD 2.9 (26.6.94)";
98 
99 /*
100  * Kernel parameter passing version
101  *	1:	first version of loadbsd
102  *	2:	needs esym location passed in a4
103  */
104 #define KERNEL_PARAMETER_VERSION	2
105 
106 #define MAXMEMSEG	16
107 struct boot_memlist {
108 	u_int	m_nseg; /* num_mem; */
109 	struct boot_memseg {
110 		u_int	ms_start;
111 		u_int	ms_size;
112 		u_short	ms_attrib;
113 		short	ms_pri;
114 	} m_seg[MAXMEMSEG];
115 };
116 struct boot_memlist memlist;
117 struct boot_memlist *kmemlist;
118 
119 
120 void get_mem_config __P((void **, u_long *, u_long *));
121 void get_cpuid __P((void));
122 void get_eclock __P((void));
123 void get_AGA __P((void));
124 void usage __P((void));
125 void verbose_usage __P((void));
126 void Version __P((void));
127 
128 extern struct ExecBase *SysBase;
129 extern char *optarg;
130 extern int optind;
131 
132 int k_flag;
133 int p_flag;
134 int t_flag;
135 int reqmemsz;
136 int S_flag;
137 u_long cpuid;
138 long eclock_freq;
139 long AGA_mode;
140 char *program_name;
141 char *kname;
142 struct ExpansionBase *ExpansionBase;
143 struct GfxBase *GfxBase;
144 
145 
146 int
147 main(argc, argv)
148 	int argc;
149 	char **argv;
150 {
151 	struct exec e;
152 	struct ConfigDev *cd, *kcd;
153 	u_long fmemsz, cmemsz;
154 	int fd, boothowto, ksize, textsz, stringsz, ncd, i, mem_ix, ch;
155 	u_short *kvers;
156 	int *nkcd;
157 	u_char *kp;
158 	void *fmem;
159 	char *esym;
160 
161 	program_name = argv[0];
162 	boothowto = RB_SINGLE;
163 
164 	if (argc < 2)
165 		usage();
166 	if ((GfxBase = (void *)OpenLibrary(GRAPHICSNAME, 0)) == NULL)
167 		err(20, "can't open graphics library");
168 	if ((ExpansionBase=(void *)OpenLibrary(EXPANSIONNAME, 0)) == NULL)
169 		err(20, "can't open expansion library");
170 
171 	while ((ch = getopt(argc, argv, "aAbc:Dhkm:ptSV")) != EOF) {
172 		switch (ch) {
173 		case 'k':
174 			k_flag = 1;
175 			break;
176 		case 'a':
177 			boothowto &= ~(RB_SINGLE);
178 			boothowto |= RB_AUTOBOOT;
179 			break;
180 		case 'b':
181 			boothowto |= RB_ASKNAME;
182 			break;
183 		case 'p':
184 			p_flag = 1;
185 			break;
186 		case 't':
187 			t_flag = 1;
188 			break;
189 		case 'm':
190 			reqmemsz = atoi(optarg) * 1024;
191 			break;
192 		case 'V':
193 			fprintf(stderr,"%s\n",_version + 6);
194 			break;
195 		case 'S':
196 			S_flag = 1;
197 			break;
198 		case 'D':
199 			boothowto |= RB_KDB;
200 			break;
201 		case 'c':
202 			cpuid = atoi(optarg) << 16;
203 			break;
204 		case 'A':
205 			AGA_mode = 1;
206 			break;
207 		case 'h':
208 			verbose_usage();
209 		default:
210 			usage();
211 		}
212 	}
213 	argc -= optind;
214 	argv += optind;
215 
216 	if (argc != 1)
217 		usage();
218 	kname = argv[0];
219 
220 	if ((fd = open(kname, 0)) < 0)
221 		err(20, "open");
222 	if (read(fd, &e, sizeof(e)) != sizeof(e))
223 		err(20, "reading exec");
224 	if (e.a_magic != NMAGIC)
225 		err(20, "unknown binary");
226 
227 	for (cd = 0, ncd = 0; cd = FindConfigDev(cd, -1, -1); ncd++)
228 		;
229 	get_mem_config(&fmem, &fmemsz, &cmemsz);
230 	get_cpuid();
231 	get_eclock();
232 	get_AGA();
233 
234 	textsz = (e.a_text + __LDPGSZ - 1) & (-__LDPGSZ);
235 	esym = NULL;
236 	ksize = textsz + e.a_data + e.a_bss + ncd * sizeof(*cd)
237 	    + 4 + memlist.m_nseg * sizeof(struct boot_memseg) + 4;
238 
239 	/*
240 	 * get symbol table size & string size
241 	 * (should check kernel version to see if it will handle it)
242 	 */
243 	if (S_flag && e.a_syms) {
244 		if (lseek(fd, e.a_text + e.a_data + e.a_syms, SEEK_CUR) <= 0
245 		    || read(fd, &stringsz, 4) != 4
246 		    || lseek(fd, sizeof(e), SEEK_SET) < 0)
247 			err(20, "lseek for symbols");
248 		ksize += e.a_syms + 4 + stringsz;
249 	}
250 
251 	kp = (u_char *)malloc(ksize);
252 	if (t_flag) {
253 		for (i = 0; i < memlist.m_nseg; ++i) {
254 			printf("mem segment %d: start=%08lx size=%08lx"
255 			    " attribute=%04lx pri=%d\n",
256 			    i + 1, memlist.m_seg[i].ms_start,
257 			    memlist.m_seg[i].ms_size,
258 			    memlist.m_seg[i].ms_attrib,
259 			    memlist.m_seg[i].ms_pri);
260 		}
261 	}
262 	if (kp == NULL)
263 		err(20, "failed malloc %d\n", ksize);
264 
265 	if (read(fd, kp, e.a_text) != e.a_text
266 	    || read(fd, kp + textsz, e.a_data) != e.a_data)
267 		err(20, "unable to read kernel image\n");
268 
269 	if (k_flag) {
270 		fmem += 4 * 1024 * 1024;
271 		fmemsz -= 4 * 1024 * 1024;
272 	}
273 
274 	if (reqmemsz && reqmemsz <= fmemsz)
275 		fmemsz = reqmemsz;
276 	if (boothowto & RB_AUTOBOOT)
277 		printf("Autobooting...");
278 	if (boothowto & RB_ASKNAME)
279 		printf("Askboot...");
280 
281 	printf("Using %d%c FASTMEM at 0x%x, %dM CHIPMEM\n",
282 	    (fmemsz & 0xfffff) ? fmemsz >> 10 : fmemsz >> 20,
283 	    (fmemsz & 0xfffff) ? 'K' : 'M', fmem, cmemsz >> 20);
284 	kvers = (u_short *)(kp + e.a_entry - 2);
285 	if (*kvers > KERNEL_PARAMETER_VERSION && *kvers != 0x4e73)
286 		err(20, "newer loadbsd required: %d\n", *kvers);
287 	if ((cpuid & AFB_68020) == 0)
288 		err(20, "cpu not supported");
289 	/*
290 	 * give them a chance to read the information...
291 	 */
292 	sleep(2);
293 
294 	bzero(kp + textsz + e.a_data, e.a_bss);
295 	/*
296 	 * If symbols wanted (and kernel can handle them),
297 	 * load symbol table & strings and set esym to end.
298 	 */
299 	nkcd = (int *)(kp + textsz + e.a_data + e.a_bss);
300 	if (*kvers != 0x4e73 && *kvers > 1 && S_flag && e.a_syms) {
301 		*nkcd++ = e.a_syms;
302 		read(fd, (char *)nkcd, e.a_syms);
303 		nkcd = (int *)((char *)nkcd + e.a_syms);
304 		read(fd, (char *)nkcd, stringsz);
305 		    nkcd = (int*)((char *)nkcd + stringsz);
306 		    esym = (char *)(textsz + e.a_data + e.a_bss
307 		    + e.a_syms + 4 + stringsz);
308 	}
309 	*nkcd = ncd;
310 
311 	kcd = (struct ConfigDev *)(nkcd + 1);
312 	while(cd = FindConfigDev(cd, -1, -1))
313 		*kcd++ = *cd;
314 
315 	kmemlist = (struct boot_memlist *)kcd;
316 	kmemlist->m_nseg = memlist.m_nseg;
317 	for (mem_ix = 0; mem_ix < memlist.m_nseg; mem_ix++)
318 		kmemlist->m_seg[mem_ix] = memlist.m_seg[mem_ix];
319 	/*
320 	 * if test option set, done
321 	 */
322 	if (t_flag)
323 		exit(0);
324 
325 	/*
326 	 * XXX AGA startup - may need more
327 	 */
328 	LoadView(NULL);		/* Don't do this if AGA_mode? */
329 	startit(kp, ksize, e.a_entry, fmem, fmemsz, cmemsz, boothowto, esym,
330 	    cpuid, eclock_freq, AGA_mode);
331 	/*NOTREACHED*/
332 }
333 
334 void
335 get_mem_config(fmem, fmemsz, cmemsz)
336 	void **fmem;
337 	u_long *fmemsz, *cmemsz;
338 {
339 	struct MemHeader *mh, *nmh;
340 	u_int segsz, seg, eseg, nmem;
341 	char mempri;
342 
343 	nmem = 0;
344 	mempri = -128;
345 	*fmemsz = 0;
346 	*cmemsz = 0;
347 
348 	/*
349 	 * walk thru the exec memory list
350 	 */
351 	Forbid();
352 	for (mh  = (void *) SysBase->MemList.lh_Head;
353 	    nmh = (void *) mh->mh_Node.ln_Succ; mh = nmh, nmem++) {
354 		memlist.m_seg[nmem].ms_attrib = mh->mh_Attributes;
355 		memlist.m_seg[nmem].ms_pri = mh->mh_Node.ln_Pri;
356 		seg = (u_int)mh->mh_Lower;
357 		eseg = (u_int)mh->mh_Upper;
358 		segsz = eseg - seg;
359 		memlist.m_seg[nmem].ms_size = segsz;
360 		memlist.m_seg[nmem].ms_start = seg;
361 
362 		if (mh->mh_Attributes & MEMF_CHIP) {
363 			/*
364 			 * there should hardly be more than one entry for
365 			 * chip mem, but handle it the same nevertheless
366 			 * cmem always starts at 0, so include vector area
367 			 */
368 			memlist.m_seg[nmem].ms_start = seg = 0;
369 			/*
370 			 * round to multiple of 512K
371 			 */
372 			segsz = (segsz + 512 * 1024 - 1) & -(512 * 1024);
373 			memlist.m_seg[nmem].ms_size = segsz;
374 			if (segsz > *cmemsz)
375 				*cmemsz = segsz;
376 			continue;
377 		}
378 		/*
379 		 * some heuristics..
380 		 */
381 		seg &= -__LDPGSZ;
382 		eseg = (eseg + __LDPGSZ - 1) & -__LDPGSZ;
383 
384 		/*
385 		 * get the mem back stolen by incore kickstart on
386 		 * A3000 with V36 bootrom.
387 		 */
388 		if (eseg == 0x07f80000)
389 			eseg = 0x08000000;
390 
391 		/*
392 		 * or by zkick on a A2000.
393 		 */
394 		if (seg == 0x280000 &&
395 		    strcmp(mh->mh_Node.ln_Name, "zkick memory") == 0)
396 			seg = 0x200000;
397 
398 		segsz = eseg - seg;
399 		memlist.m_seg[nmem].ms_start = seg;
400 		memlist.m_seg[nmem].ms_size = segsz;
401 		/*
402 		 *  If this segment is smaller than 2M,
403 		 *  don't use it to load the kernel
404 		 */
405 		if (segsz < 2 * 1024 * 1024)
406 			continue;
407 		/*
408 		 * if p_flag is set, select memory by priority
409 		 * instead of size
410 		 */
411 		if ((!p_flag && segsz > *fmemsz) || (p_flag &&
412 		   mempri <= mh->mh_Node.ln_Pri && segsz > *fmemsz)) {
413 			*fmemsz = segsz;
414 			*fmem = (void *)seg;
415 			mempri = mh->mh_Node.ln_Pri;
416 		}
417 	}
418 	memlist.m_nseg = nmem;
419 	Permit();
420 }
421 
422 /*
423  * Try to determine the machine ID by searching the resident module list
424  * for modules only present on specific machines.  (Thanks, Bill!)
425  */
426 void
427 get_cpuid()
428 {
429 	u_long *rl;
430 	struct Resident *rm;
431 
432 	cpuid |= SysBase->AttnFlags;	/* get FPU and CPU flags */
433 	if (cpuid & 0xffff0000) {
434 		switch (cpuid >> 16) {
435 		case 500:
436 		case 600:
437 		case 1000:
438 		case 1200:
439 		case 2000:
440 		case 3000:
441 		case 4000:
442 			return;
443 		default:
444 			printf("machine Amiga %d is not recognized\n",
445 			    cpuid >> 16);
446 			exit(1);
447 		}
448 	}
449 	rl = (u_long *)SysBase->ResModules;
450 	if (rl == NULL)
451 		return;
452 
453 	while (*rl) {
454 		rm = (struct Resident *) *rl;
455 		if (strcmp(rm->rt_Name, "A4000 Bonus") == 0 ||
456 		    strcmp(rm->rt_Name, "A1000 Bonus") == 0) {
457 			cpuid |= 4000 << 16;
458 			break;
459 		}
460 		if (strcmp(rm->rt_Name, "A3000 bonus") == 0 ||
461 		    strcmp(rm->rt_Name, "A3000 Bonus") == 0) {
462 			cpuid |= 3000 << 16;
463 			break;
464 		}
465 		if (strcmp(rm->rt_Name, "card.resource") == 0) {
466 			cpuid |= 1200 << 16;	/* or A600 :-) */
467 			break;
468 		}
469 		++rl;
470 	}
471 	/*
472 	 * Nothing found, it's probably an A2000 or A500
473 	 */
474 	if (*rl == 0)
475 		cpuid |= 2000 << 16;
476 }
477 
478 void
479 get_eclock()
480 {
481 	/* Fix for 1.3 startups? */
482 	if (SysBase->LibNode.lib_Version > 36)
483 		eclock_freq = SysBase->ex_EClockFrequency;
484 	else
485 		eclock_freq = (GfxBase->DisplayFlags & PAL) ?
486 		    709379 : 715909;
487 }
488 
489 void
490 get_AGA()
491 {
492 	/*
493 	 * Determine if an AGA mode is active
494 	 */
495 }
496 
497 
498 asm("
499 	.set	ABSEXECBASE,4
500 
501 	.text
502 	.globl	_startit
503 
504 _startit:
505 	movel	sp,a3
506 	movel	4:w,a6
507 	lea	pc@(start_super-.+2),a5
508 	jmp	a6@(-0x1e)		| supervisor-call
509 
510 start_super:
511 	movew	#0x2700,sr
512 
513 	| the BSD kernel wants values into the following registers:
514 	| a0:  fastmem-start
515 	| d0:  fastmem-size
516 	| d1:  chipmem-size
517 	| d3:  AGA mode enable
518 	| d4:  E clock frequency
519 	| d5:  AttnFlags (cpuid)
520 	| d7:  boothowto
521 	| a4:  esym location
522 	| All other registers zeroed for possible future requirements.
523 
524 	lea	pc@(_startit-.+2),sp	| make sure we have a good stack ***
525 	movel	a3@(4),a1		| loaded kernel
526 	movel	a3@(8),d2		| length of loaded kernel
527 |	movel	a3@(12),sp		| entry point in stack pointer
528 	movel	a3@(12),sp@-		| push entry point		***
529 	movel	a3@(16),a0		| fastmem-start
530 	movel	a3@(20),d0		| fastmem-size
531 	movel	a3@(24),d1		| chipmem-size
532 	movel	a3@(28),d7		| boothowto
533 	movel	a3@(32),a4		| esym
534 	movel	a3@(36),d5		| cpuid
535 	movel	a3@(40),d4		| E clock frequency
536 	movel	a3@(44),d3		| AGA mode enable
537 	subl	a5,a5			| target, load to 0
538 
539 	btst	#3,(ABSEXECBASE)@(0x129) | AFB_68040,SysBase->AttnFlags
540 	beq	not040
541 
542 | Turn off 68040 MMU
543 
544 	.word 0x4e7b,0xd003		| movec a5,tc
545 	.word 0x4e7b,0xd806		| movec a5,urp
546 	.word 0x4e7b,0xd807		| movec a5,srp
547 	.word 0x4e7b,0xd004		| movec a5,itt0
548 	.word 0x4e7b,0xd005		| movec a5,itt1
549 	.word 0x4e7b,0xd006		| movec a5,dtt0
550 	.word 0x4e7b,0xd007		| movec a5,dtt1
551 	bra	nott
552 
553 not040:
554 	lea	pc@(zero-.+2),a3
555 	pmove	a3@,tc			| Turn off MMU
556 	lea	pc@(nullrp-.+2),a3
557 	pmove	a3@,crp			| Turn off MMU some more
558 	pmove	a3@,srp			| Really, really, turn off MMU
559 
560 | Turn off 68030 TT registers
561 
562 	btst	#2,(ABSEXECBASE)@(0x129) | AFB_68030,SysBase->AttnFlags
563 	beq	nott			| Skip TT registers if not 68030
564 	lea	pc@(zero-.+2),a3
565 	.word 0xf013,0x0800		| pmove a3@,tt0 (gas only knows about 68851 ops..)
566 	.word 0xf013,0x0c00		| pmove a3@,tt1 (gas only knows about 68851 ops..)
567 
568 nott:
569 
570 	movew	#(1<<9),0xdff096	| disable DMA
571 
572 L0:
573 	moveb	a1@+,a5@+
574 	subl	#1,d2
575 	bcc	L0
576 
577 
578 	moveq	#0,d2			| zero out unused registers
579 	moveq	#0,d6			| (might make future compatibility
580 	movel	d6,a1			|  would have known contents)
581 	movel	d6,a2
582 	movel	d6,a3
583 	movel	d6,a5
584 	movel	d6,a6
585 |	jmp	sp@			| jump to kernel entry point
586 	rts				| enter kernel at address on stack ***
587 
588 
589 | A do-nothing MMU root pointer (includes the following long as well)
590 
591 nullrp:	.long	0x7fff0001
592 zero:	.long	0
593 
594 
595 ");
596 
597 void
598 usage()
599 {
600 	fprintf(stderr, "usage: %s [-abkptADSV] [-c machine] [-m mem] kernel\n",
601 	    program_name);
602 	exit(1);
603 }
604 
605 
606 void
607 verbose_usage()
608 {
609 	fprintf(stderr, "
610 NAME
611 \t%s - loads NetBSD from amiga dos.
612 SYNOPSIS
613 \t%s [-abkptDSV] [-c machine] [-m mem] kernel
614 OPTIONS
615 \t-a  Boot up to multiuser mode.
616 \t-b  Ask for which root device.
617 \t    Its possible to have multiple roots and choose between them.
618 \t-c  Set machine type. [e.g 3000]
619 \t-k  Reserve the first 4M of fast mem [Some one else
620 \t    is going to have to answer what that it is used for].
621 \t-m  Tweak amount of available memory, for finding minimum amount
622 \t    of memory required to run. Sets fastmem size to specified
623 \t    size in Kbytes.
624 \t-p  Use highest priority fastmem segement instead of the largest
625 \t    segment. The higher priority segment is usually faster
626 \t    (i.e. 32 bit memory), but some people have smaller amounts
627 \t    of 32 bit memory.
628 \t-t  This is a *test* option.  It prints out the memory
629 \t    list information being passed to the kernel and also
630 \t    exits without actually starting NetBSD.
631 \t-S  Include kernel symbol table.
632 \t-D  Enter debugger
633 \t-A  Use AGA display mode, if available.
634 \t-V  Version of loadbsd program.
635 HISTORY
636 \tThis version supports Kernel version 720 +\n",
637       program_name, program_name);
638       exit(1);
639 }
640 
641 
642 void
643 _Vdomessage(doexit, eval, doerrno, fmt, args)
644 	int doexit, doerrno, eval;
645 	const char *fmt;
646 	va_list args;
647 {
648 	fprintf(stderr, "%s: ", program_name);
649 	if (fmt) {
650 		vfprintf(stderr, fmt, args);
651 		fprintf(stderr, ": ");
652 	}
653 	if (doerrno && errno < sys_nerr) {
654 		fprintf(stderr, "%s", strerror(errno));
655 		if (errno == EINTR || errno == 0) {
656 			int  sigs;
657 			sigpending((sigset_t *)&sigs);
658 			printf("%x\n", sigs);
659 		}
660 	}
661 	fprintf(stderr, "\n");
662 	if (doexit)
663 		exit(eval);
664 }
665 
666 void
667 err(int eval, const char *fmt, ...)
668 {
669 	va_list ap;
670 	va_start(ap, fmt);
671 	_Vdomessage(1, eval, 1, fmt, ap);
672 	/*NOTREACHED*/
673 }
674 
675 void
676 errx(int eval, const char *fmt, ...)
677 {
678 	va_list ap;
679 	va_start(ap, fmt);
680 	_Vdomessage(1, eval, 0, fmt, ap);
681 	/*NOTREACHED*/
682 }
683 
684 void
685 warn(const char *fmt, ...)
686 {
687 	va_list ap;
688 	va_start(ap, fmt);
689 	_Vdomessage(0, 0, 1, fmt, ap);
690 	va_end(ap);
691 }
692 
693 void
694 warnx(const char *fmt, ...)
695 {
696 	va_list ap;
697 	va_start(ap, fmt);
698 	_Vdomessage(0, 0, 0, fmt, ap);
699 	va_end(ap);
700 }
701