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