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