xref: /plan9-contrib/sys/src/9/pc/main.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"io.h"
7 #include	"ureg.h"
8 #include	"init.h"
9 #include	<ctype.h>
10 
11 
12 uchar *sp;	/* stack pointer for /boot */
13 
14 extern PCArch nsx20, generic, ncr3170;
15 
16 PCArch *arch;
17 PCArch *knownarch[] =
18 {
19 	&nsx20,
20 	&ncr3170,
21 	&generic,
22 };
23 
24 /* where b.com leaves configuration info */
25 #define BOOTARGS	((char*)(KZERO|1024))
26 #define	BOOTARGSLEN	1024
27 #define	MAXCONF		32
28 
29 char bootdisk[NAMELEN];
30 char *confname[MAXCONF];
31 char *confval[MAXCONF];
32 int nconf;
33 
34 /* memory map */
35 #define MAXMEG 64
36 char mmap[MAXMEG+2];
37 
38 void
39 main(void)
40 {
41 	ident();
42 	i8042a20();		/* enable address lines 20 and up */
43 	machinit();
44 	active.exiting = 0;
45 	active.machs = 1;
46 	confinit();
47 	xinit();
48 	dmainit();
49 	screeninit();
50 	printinit();
51 	mmuinit();
52 	pageinit();
53 	trapinit();
54 	mathinit();
55 	clockinit();
56 	printcpufreq();
57 	faultinit();
58 	kbdinit();
59 	procinit0();
60 	initseg();
61 	streaminit();
62 	chandevreset();
63 	swapinit();
64 	userinit();
65 	schedinit();
66 }
67 
68 /*
69  *  This tries to capture architecture dependencies since things
70  *  like power management/reseting/mouse are outside the hardware
71  *  model.
72  */
73 void
74 ident(void)
75 {
76 	char *id = (char*)(ROMBIOS + 0xFF40);
77 	PCArch **p;
78 
79 	for(p = knownarch; *p != &generic; p++)
80 		if(strncmp((*p)->id, id, strlen((*p)->id)) == 0)
81 			break;
82 	arch = *p;
83 }
84 
85 void
86 machinit(void)
87 {
88 	int n;
89 
90 	n = m->machno;
91 	memset(m, 0, sizeof(Mach));
92 	m->machno = n;
93 	m->mmask = 1<<m->machno;
94 }
95 
96 ulong garbage;
97 
98 void
99 init0(void)
100 {
101 	int i;
102 	char tstr[32];
103 
104 	u->nerrlab = 0;
105 	m->proc = u->p;
106 	u->p->state = Running;
107 	u->p->mach = m;
108 
109 	spllo();
110 
111 	/*
112 	 * These are o.k. because rootinit is null.
113 	 * Then early kproc's will have a root and dot.
114 	 */
115 	u->slash = (*devtab[0].attach)(0);
116 	u->dot = clone(u->slash, 0);
117 
118 	kproc("alarm", alarmkproc, 0);
119 	chandevinit();
120 
121 	if(!waserror()){
122 		strcpy(tstr, arch->id);
123 		strcat(tstr, " %s");
124 		ksetterm(tstr);
125 		ksetenv("cputype", "386");
126 		for(i = 0; i < nconf; i++)
127 			if(confname[i])
128 				ksetenv(confname[i], confval[i]);
129 		poperror();
130 	}
131 	touser(sp);
132 }
133 
134 void
135 userinit(void)
136 {
137 	Proc *p;
138 	Segment *s;
139 	User *up;
140 	KMap *k;
141 	Page *pg;
142 
143 	p = newproc();
144 	p->pgrp = newpgrp();
145 	p->egrp = smalloc(sizeof(Egrp));
146 	p->egrp->ref = 1;
147 	p->fgrp = smalloc(sizeof(Fgrp));
148 	p->fgrp->ref = 1;
149 	p->procmode = 0640;
150 
151 	strcpy(p->text, "*init*");
152 	strcpy(p->user, eve);
153 	p->fpstate = FPinit;
154 	fpoff();
155 
156 	/*
157 	 * Kernel Stack
158 	 *
159 	 * N.B. The -12 for the stack pointer is important.
160 	 *	4 bytes for gotolabel's return PC
161 	 */
162 	p->sched.pc = (ulong)init0;
163 	p->sched.sp = USERADDR + BY2PG - 4;
164 	p->upage = newpage(1, 0, USERADDR|(p->pid&0xFFFF));
165 
166 	/*
167 	 * User
168 	 */
169 	k = kmap(p->upage);
170 	up = (User*)VA(k);
171 	up->p = p;
172 	kunmap(k);
173 
174 	/*
175 	 * User Stack
176 	 */
177 	s = newseg(SG_STACK, USTKTOP-BY2PG, 1);
178 	p->seg[SSEG] = s;
179 	pg = newpage(1, 0, USTKTOP-BY2PG);
180 	segpage(s, pg);
181 	k = kmap(pg);
182 	bootargs(VA(k));
183 	kunmap(k);
184 
185 	/*
186 	 * Text
187 	 */
188 	s = newseg(SG_TEXT, UTZERO, 1);
189 	p->seg[TSEG] = s;
190 	segpage(s, newpage(1, 0, UTZERO));
191 	k = kmap(s->map[0]->pages[0]);
192 	memmove((ulong*)VA(k), initcode, sizeof initcode);
193 	kunmap(k);
194 
195 	ready(p);
196 }
197 
198 uchar *
199 pusharg(char *p)
200 {
201 	int n;
202 
203 	n = strlen(p)+1;
204 	sp -= n;
205 	memmove(sp, p, n);
206 	return sp;
207 }
208 
209 void
210 bootargs(ulong base)
211 {
212  	int i, ac;
213 	uchar *av[32];
214 	uchar **lsp;
215 	char *cp = BOOTLINE;
216 	char buf[64];
217 
218 	sp = (uchar*)base + BY2PG - MAXSYSARG*BY2WD;
219 
220 	ac = 0;
221 	av[ac++] = pusharg("/386/9pc");
222 	cp[64] = 0;
223 	buf[0] = 0;
224 
225 	/*
226 	 *  decode the b.com bootline and convert to
227 	 *  a disk device name to pass to the boot
228 	 */
229 	if(strncmp(cp, "fd!", 3) == 0){
230 		sprint(buf, "local!#f/fd%ddisk", atoi(cp+3));
231 		av[ac++] = pusharg(buf);
232 	} else if(strncmp(cp, "h!", 2) == 0){
233 		sprint(buf, "local!#H/hd%dfs", atoi(cp+2));
234 		av[ac++] = pusharg(buf);
235 	} else if(strncmp(cp, "hd!", 3) == 0){
236 		sprint(buf, "local!#H/hd%ddisk", atoi(cp+3));
237 		av[ac++] = pusharg(buf);
238 	} else if(strncmp(cp, "s!", 2) == 0){
239 		sprint(buf, "local!#w%d/sd%dfs", atoi(cp+2), atoi(cp+2));
240 		av[ac++] = pusharg(buf);
241 	} else if(strncmp(cp, "sd!", 3) == 0){
242 		sprint(buf, "local!#w%d/sd%ddisk", atoi(cp+3), atoi(cp+3));
243 		av[ac++] = pusharg(buf);
244 	} else if(getconf("bootdisk") == 0){
245 		if(conf.nhard){
246 			sprint(buf, "local!#H/hd0disk");
247 			av[ac++] = pusharg(buf);
248 		} else{
249 			sprint(buf, "local!#w/sd0disk");
250 			av[ac++] = pusharg(buf);
251 		}
252 	}
253 	if(buf[0]){
254 		cp = strchr(buf, '!');
255 		if(cp){
256 			strcpy(bootdisk, cp+1);
257 			addconf("bootdisk", bootdisk);
258 		}
259 	}
260 
261 	/* 4 byte word align stack */
262 	sp = (uchar*)((ulong)sp & ~3);
263 
264 	/* build argc, argv on stack */
265 	sp -= (ac+1)*sizeof(sp);
266 	lsp = (uchar**)sp;
267 	for(i = 0; i < ac; i++)
268 		*lsp++ = av[i] + ((USTKTOP - BY2PG) - base);
269 	*lsp = 0;
270 	sp += (USTKTOP - BY2PG) - base - sizeof(ulong);
271 }
272 
273 Conf	conf;
274 
275 void
276 addconf(char *name, char *val)
277 {
278 	if(nconf >= MAXCONF)
279 		return;
280 	confname[nconf] = name;
281 	confval[nconf] = val;
282 	nconf++;
283 }
284 
285 char*
286 getconf(char *name)
287 {
288 	int i;
289 
290 	for(i = 0; i < nconf; i++)
291 		if(strcmp(confname[i], name) == 0)
292 			return confval[i];
293 	return 0;
294 }
295 
296 /*
297  *  look for unused address space in 0xC8000 to 1 meg
298  */
299 void
300 romscan(void)
301 {
302 	uchar *p;
303 
304 	p = (uchar*)(KZERO+0xC8000);
305 	while(p < (uchar*)(KZERO+0xE0000)){
306 		p[0] = 0x55;
307 		p[1] = 0xAA;
308 		p[2] = 4;
309 		if(p[0] != 0x55 || p[1] != 0xAA){
310 			putisa(PADDR(p), 2048);
311 			p += 2048;
312 			continue;
313 		}
314 		p += p[2]*512;
315 	}
316 
317 	p = (uchar*)(KZERO+0xE0000);
318 	if(p[0] != 0x55 || p[1] != 0xAA)
319 		putisa(PADDR(p), 64*1024);
320 }
321 
322 
323 void
324 confinit(void)
325 {
326 	long x, i, j, n;
327 	int pcnt;
328 	ulong ktop;
329 	char *cp;
330 	char *line[MAXCONF];
331 
332 	pcnt = 0;
333 
334 	/*
335 	 *  parse configuration args from dos file p9rc
336 	 */
337 	cp = BOOTARGS;	/* where b.com leaves plan9.ini */
338 	cp[BOOTARGSLEN-1] = 0;
339 	n = getfields(cp, line, MAXCONF, "\n");
340 	for(j = 0; j < n; j++){
341 		cp = strchr(line[j], '\r');
342 		if(cp)
343 			*cp = 0;
344 		cp = strchr(line[j], '=');
345 		if(cp == 0)
346 			continue;
347 		*cp++ = 0;
348 		if(cp - line[j] >= NAMELEN+1)
349 			*(line[j]+NAMELEN-1) = 0;
350 		confname[nconf] = line[j];
351 		confval[nconf] = cp;
352 		if(strcmp(confname[nconf], "kernelpercent") == 0)
353 			pcnt = 100 - atoi(confval[nconf]);
354 		nconf++;
355 	}
356 	/*
357 	 *  size memory above 1 meg. Kernel sits at 1 meg.  We
358 	 *  only recognize MB size chunks.
359 	 */
360 	memset(mmap, ' ', sizeof(mmap));
361 	x = 0x12345678;
362 	for(i = 1; i <= MAXMEG; i++){
363 		/*
364 		 *  write the first & last word in a megabyte of memory
365 		 */
366 		*mapaddr(KZERO|(i*MB)) = x;
367 		*mapaddr(KZERO|((i+1)*MB-BY2WD)) = x;
368 
369 		/*
370 		 *  write the first and last word in all previous megs to
371 		 *  handle address wrap around
372 		 */
373 		for(j = 1; j < i; j++){
374 			*mapaddr(KZERO|(j*MB)) = ~x;
375 			*mapaddr(KZERO|((j+1)*MB-BY2WD)) = ~x;
376 		}
377 
378 		/*
379 		 *  check for correct value
380 		 */
381 		if(*mapaddr(KZERO|(i*MB)) == x && *mapaddr(KZERO|((i+1)*MB-BY2WD)) == x){
382 			mmap[i] = 'x';
383 			/*
384 			 *  zero memory to set ECC but skip over the kernel
385 			 */
386 			if(i != 1)
387 				for(j = 0; j < MB/BY2PG; j += BY2PG)
388 					memset(mapaddr(KZERO|(i*MB+j)), 0, BY2PG);
389 		}
390 		x += 0x3141526;
391 	}
392 	/*
393 	 *  bank0 usually goes from the end of kernel bss to the end of memory
394 	 */
395 	ktop = PGROUND((ulong)end);
396 	ktop = PADDR(ktop);
397 	conf.base0 = ktop;
398 	for(i = 1; mmap[i] == 'x'; i++)
399 		;
400 	conf.npage0 = (i*MB - ktop)/BY2PG;
401 	conf.topofmem = i*MB;
402 
403 	/*
404 	 *  bank1 usually goes from the end of BOOTARGS to 640k
405 	 */
406 	conf.base1 = (ulong)(BOOTARGS+BOOTARGSLEN);
407 	conf.base1 = PGROUND(conf.base1);
408 	conf.base1 = PADDR(conf.base1);
409 	conf.npage1 = (640*1024-conf.base1)/BY2PG;
410 
411 	/*
412 	 *  if there is a hole in memory (due to a shadow BIOS) make the
413 	 *  memory after the hole be bank 1. The memory from 0 to 640k
414 	 *  is lost.
415 	 */
416 	for(; i <= MAXMEG; i++)
417 		if(mmap[i] == 'x'){
418 			conf.base1 = i*MB;
419 			for(j = i+1; mmap[j] == 'x'; j++)
420 				;
421 			conf.npage1 = (j - i)*MB/BY2PG;
422 			conf.topofmem = j*MB;
423 			break;
424 		}
425 
426 	/*
427  	 *  add address space holes holes under 16 meg to available
428 	 *  isa space.
429 	 */
430 	romscan();
431 	if(conf.topofmem < 16*MB)
432 		putisa(conf.topofmem, 16*MB - conf.topofmem);
433 
434 	conf.npage = conf.npage0 + conf.npage1;
435 	conf.ldepth = 0;
436 	if(pcnt < 10)
437 		pcnt = 70;
438 	conf.upages = (conf.npage*pcnt)/100;
439 
440 	conf.nproc = 30 + ((conf.npage*BY2PG)/MB)*8;
441 	conf.monitor = 1;
442 	conf.nswap = conf.nproc*80;
443 	conf.nimage = 50;
444 	switch(x86()){
445 	case 3:
446 		conf.copymode = 1;	/* copy on reference */
447 		break;
448 	default:
449 		conf.copymode = 0;	/* copy on write */
450 		break;
451 	}
452 	conf.nfloppy = 2;
453 	conf.nhard = 2;
454 	conf.nmach = 1;
455 }
456 
457 char *mathmsg[] =
458 {
459 	"invalid",
460 	"denormalized",
461 	"div-by-zero",
462 	"overflow",
463 	"underflow",
464 	"precision",
465 	"stack",
466 	"error",
467 };
468 
469 /*
470  *  math coprocessor error
471  */
472 void
473 matherror(Ureg *ur, void *a)
474 {
475 	ulong status;
476 	int i;
477 	char *msg;
478 	char note[ERRLEN];
479 
480 	USED(a);
481 
482 	/*
483 	 *  a write cycle to port 0xF0 clears the interrupt latch attached
484 	 *  to the error# line from the 387
485 	 */
486 	outb(0xF0, 0xFF);
487 
488 	/*
489 	 *  save floating point state to check out error
490 	 */
491 	fpenv(&u->fpsave);
492 	status = u->fpsave.status;
493 
494 	msg = 0;
495 	for(i = 0; i < 8; i++)
496 		if((1<<i) & status){
497 			msg = mathmsg[i];
498 			sprint(note, "sys: fp: %s fppc=0x%lux", msg, u->fpsave.pc);
499 			postnote(u->p, 1, note, NDebug);
500 			break;
501 		}
502 	if(msg == 0){
503 		sprint(note, "sys: fp: unknown fppc=0x%lux", u->fpsave.pc);
504 		postnote(u->p, 1, note, NDebug);
505 	}
506 	if(ur->pc & KZERO)
507 		panic("fp: status %lux fppc=0x%lux pc=0x%lux", status,
508 			u->fpsave.pc, ur->pc);
509 }
510 
511 /*
512  *  math coprocessor emulation fault
513  */
514 void
515 mathemu(Ureg *ur, void *a)
516 {
517 	USED(ur, a);
518 
519 	switch(u->p->fpstate){
520 	case FPinit:
521 		fpinit();
522 		u->p->fpstate = FPactive;
523 		break;
524 	case FPinactive:
525 		fprestore(&u->fpsave);
526 		u->p->fpstate = FPactive;
527 		break;
528 	case FPactive:
529 		panic("math emu", 0);
530 		break;
531 	}
532 }
533 
534 /*
535  *  math coprocessor segment overrun
536  */
537 void
538 mathover(Ureg *ur, void *a)
539 {
540 	USED(ur, a);
541 
542 print("sys: fp: math overrun pc 0x%lux pid %d\n", ur->pc, u->p->pid);
543 	pexit("math overrun", 0);
544 }
545 
546 void
547 mathinit(void)
548 {
549 	setvec(Matherr1vec, matherror, 0);
550 	setvec(Matherr2vec, matherror, 0);
551 	setvec(Mathemuvec, mathemu, 0);
552 	setvec(Mathovervec, mathover, 0);
553 }
554 
555 /*
556  *  set up floating point for a new process
557  */
558 void
559 procsetup(Proc *p)
560 {
561 	p->fpstate = FPinit;
562 	fpoff();
563 }
564 
565 /*
566  *  Save the mach dependent part of the process state.
567  */
568 void
569 procsave(Proc *p)
570 {
571 	if(p->fpstate == FPactive){
572 		if(p->state == Moribund)
573 			fpoff();
574 		else
575 			fpsave(&u->fpsave);
576 		p->fpstate = FPinactive;
577 	}
578 }
579 
580 /*
581  *  Restore what procsave() saves
582  */
583 void
584 procrestore(Proc *p)
585 {
586 	USED(p);
587 }
588 
589 
590 /*
591  *  the following functions all are slightly different from
592  *  PC to PC.
593  */
594 
595 /*
596  *  reset the i387 chip
597  */
598 void
599 exit(int ispanic)
600 {
601 	u = 0;
602 	wipekeys();
603 	print("exiting\n");
604 	if(ispanic){
605 		if(cpuserver)
606 			delay(10000);
607 		else
608 			for(;;);
609 	}
610 
611 	(*arch->reset)();
612 }
613 
614 /*
615  *  set cpu speed
616  *	0 == low speed
617  *	1 == high speed
618  */
619 int
620 cpuspeed(int speed)
621 {
622 	if(arch->cpuspeed)
623 		return (*arch->cpuspeed)(speed);
624 	else
625 		return 0;
626 }
627 
628 /*
629  *  f == frequency (Hz)
630  *  d == duration (ms)
631  */
632 void
633 buzz(int f, int d)
634 {
635 	if(arch->buzz)
636 		(*arch->buzz)(f, d);
637 }
638 
639 /*
640  *  each bit in val stands for a light
641  */
642 void
643 lights(int val)
644 {
645 	if(arch->lights)
646 		(*arch->lights)(val);
647 }
648 
649 /*
650  *  power to serial port
651  *	onoff == 1 means on
652  *	onoff == 0 means off
653  */
654 int
655 serial(int onoff)
656 {
657 	if(arch->serialpower)
658 		return (*arch->serialpower)(onoff);
659 	else
660 		return 0;
661 }
662 
663 /*
664  *  power to modem
665  *	onoff == 1 means on
666  *	onoff == 0 means off
667  */
668 int
669 modem(int onoff)
670 {
671 	if(arch->modempower)
672 		return (*arch->modempower)(onoff);
673 	else
674 		return 0;
675 }
676 
677 int
678 parseether(uchar *to, char *from)
679 {
680 	char nip[4];
681 	char *p;
682 	int i;
683 
684 	p = from;
685 	while(*p == ' ')
686 		++p;
687 	for(i = 0; i < 6; i++){
688 		if(*p == 0)
689 			return -1;
690 		nip[0] = *p++;
691 		if(*p == 0)
692 			return -1;
693 		nip[1] = *p++;
694 		nip[2] = 0;
695 		to[i] = strtoul(nip, 0, 16);
696 		if(*p == ':')
697 			p++;
698 	}
699 	return 0;
700 }
701 
702 int
703 isaconfig(char *class, int ctlrno, ISAConf *isa)
704 {
705 	char cc[NAMELEN], *p, *q;
706 	int n;
707 
708 	sprint(cc, "%s%d", class, ctlrno);
709 	for(n = 0; n < nconf; n++){
710 		if(strncmp(confname[n], cc, NAMELEN))
711 			continue;
712 		p = confval[n];
713 		while(*p){
714 			while(*p == ' ' || *p == '\t')
715 				p++;
716 			if(*p == '\0')
717 				break;
718 			if(strncmp(p, "type=", 5) == 0){
719 				p += 5;
720 				for(q = isa->type; q < &isa->type[NAMELEN-1]; q++){
721 					if(*p == '\0' || *p == ' ' || *p == '\t')
722 						break;
723 					*q = *p++;
724 				}
725 				*q = '\0';
726 			}
727 			else if(strncmp(p, "port=", 5) == 0)
728 				isa->port = strtoul(p+5, &p, 0);
729 			else if(strncmp(p, "irq=", 4) == 0)
730 				isa->irq = strtoul(p+4, &p, 0);
731 			else if(strncmp(p, "mem=", 4) == 0)
732 				isa->mem = strtoul(p+4, &p, 0);
733 			else if(strncmp(p, "size=", 5) == 0)
734 				isa->size = strtoul(p+5, &p, 0);
735 			else if(strncmp(p, "dma=", 4) == 0)
736 				isa->dma = strtoul(p+4, &p, 0);
737 			else if(strncmp(p, "ea=", 3) == 0){
738 				if(parseether(isa->ea, p+3) == -1)
739 					memset(isa->ea, 0, 6);
740 			}
741 			while(*p && *p != ' ' && *p != '\t')
742 				p++;
743 		}
744 		return 1;
745 	}
746 	return 0;
747 }
748 
749 static void
750 pcfloppyintr(Ureg *ur, void *a)
751 {
752 	USED(a);
753 
754 	floppyintr(ur);
755 }
756 
757 void
758 floppysetup0(FController *fl)
759 {
760 	USED(fl);
761 }
762 
763 void
764 floppysetup1(FController *fl)
765 {
766 	uchar equip;
767 
768 	/*
769 	 *  read nvram for types of floppies 0 & 1
770 	 */
771 	equip = nvramread(0x10);
772 	if(conf.nfloppy > 0){
773 		fl->d[0].dt = (equip >> 4) & 0xf;
774 		floppysetdef(&fl->d[0]);
775 	}
776 	if(conf.nfloppy > 1){
777 		fl->d[1].dt = equip & 0xf;
778 		floppysetdef(&fl->d[1]);
779 	}
780 
781 	setvec(Floppyvec, pcfloppyintr, 0);
782 }
783 
784 /*
785  *  eject disk ( unknown on safari )
786  */
787 void
788 floppyeject(FDrive *dp)
789 {
790 	floppyon(dp);
791 	dp->vers++;
792 	floppyoff(dp);
793 }
794 
795 int
796 floppyexec(char *a, long b, int c)
797 {
798 	USED(a, b, c);
799 	return b;
800 }
801 
802 int
803 cistrcmp(char *a, char *b)
804 {
805 	int ac, bc;
806 
807 	for(;;){
808 		ac = *a++;
809 		bc = *b++;
810 
811 		if(ac >= 'A' && ac <= 'Z')
812 			ac = 'a' + (ac - 'A');
813 		if(bc >= 'A' && bc <= 'Z')
814 			bc = 'a' + (bc - 'A');
815 		ac -= bc;
816 		if(ac)
817 			return ac;
818 		if(bc == 0)
819 			break;
820 	}
821 	return 0;
822 }
823