xref: /plan9-contrib/sys/src/9k/k10/devarch.c (revision 6c88371c8bad86eb867bc4823251f9f00b69c946)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7 
8 #include "ureg.h"
9 
10 typedef struct IOMap IOMap;
11 struct IOMap
12 {
13 	IOMap	*next;
14 	int	reserved;
15 	char	tag[13];
16 	ulong	start;
17 	ulong	end;
18 };
19 
20 static struct
21 {
22 	Lock;
23 	IOMap	*map;
24 	IOMap	*free;
25 	IOMap	maps[32];		// some initial free maps
26 
27 	QLock	ql;			// lock for reading map
28 } iomap;
29 
30 enum {
31 	Qdir = 0,
32 	Qioalloc = 1,
33 	Qiob,
34 	Qiow,
35 	Qiol,
36 	Qbase,
37 
38 	Qmax = 16,
39 };
40 
41 typedef long Rdwrfn(Chan*, void*, long, vlong);
42 
43 static Rdwrfn *readfn[Qmax];
44 static Rdwrfn *writefn[Qmax];
45 
46 static Dirtab archdir[Qmax] = {
47 	".",		{ Qdir, 0, QTDIR },	0,	0555,
48 	"ioalloc",	{ Qioalloc, 0 },	0,	0444,
49 	"iob",		{ Qiob, 0 },		0,	0660,
50 	"iow",		{ Qiow, 0 },		0,	0660,
51 	"iol",		{ Qiol, 0 },		0,	0660,
52 };
53 Lock archwlock;	/* the lock is only for changing archdir */
54 int narchdir = Qbase;
55 
56 /*
57  * Add a file to the #P listing.  Once added, you can't delete it.
58  * You can't add a file with the same name as one already there,
59  * and you get a pointer to the Dirtab entry so you can do things
60  * like change the Qid version.  Changing the Qid path is disallowed.
61  */
62 Dirtab*
addarchfile(char * name,int perm,Rdwrfn * rdfn,Rdwrfn * wrfn)63 addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn)
64 {
65 	int i;
66 	Dirtab d;
67 	Dirtab *dp;
68 
69 	memset(&d, 0, sizeof d);
70 	strcpy(d.name, name);
71 	d.perm = perm;
72 
73 	lock(&archwlock);
74 	if(narchdir >= Qmax){
75 		unlock(&archwlock);
76 		return nil;
77 	}
78 
79 	for(i=0; i<narchdir; i++)
80 		if(strcmp(archdir[i].name, name) == 0){
81 			unlock(&archwlock);
82 			return nil;
83 		}
84 
85 	d.qid.path = narchdir;
86 	archdir[narchdir] = d;
87 	readfn[narchdir] = rdfn;
88 	writefn[narchdir] = wrfn;
89 	dp = &archdir[narchdir++];
90 	unlock(&archwlock);
91 
92 	return dp;
93 }
94 
95 void
ioinit(void)96 ioinit(void)
97 {
98 	char *excluded;
99 	int i;
100 
101 	for(i = 0; i < nelem(iomap.maps)-1; i++)
102 		iomap.maps[i].next = &iomap.maps[i+1];
103 	iomap.maps[i].next = nil;
104 	iomap.free = iomap.maps;
105 
106 	/*
107 	 * Someone needs to explain why this was here...
108 	 */
109 	ioalloc(0x0fff, 1, 0, "dummy");	// i82557 is at 0x1000, the dummy
110 					// entry is needed for swappable devs.
111 
112 	if ((excluded = getconf("ioexclude")) != nil) {
113 		char *s;
114 
115 		s = excluded;
116 		while (s && *s != '\0' && *s != '\n') {
117 			char *ends;
118 			int io_s, io_e;
119 
120 			io_s = (int)strtol(s, &ends, 0);
121 			if (ends == nil || ends == s || *ends != '-') {
122 				print("ioinit: cannot parse option string\n");
123 				break;
124 			}
125 			s = ++ends;
126 
127 			io_e = (int)strtol(s, &ends, 0);
128 			if (ends && *ends == ',')
129 				*ends++ = '\0';
130 			s = ends;
131 
132 			ioalloc(io_s, io_e - io_s + 1, 0, "pre-allocated");
133 		}
134 	}
135 
136 }
137 
138 // Reserve a range to be ioalloced later.
139 // This is in particular useful for exchangable cards, such
140 // as pcmcia and cardbus cards.
141 int
ioreserve(int,int size,int align,char * tag)142 ioreserve(int, int size, int align, char *tag)
143 {
144 	IOMap *map, **l;
145 	int i, port;
146 
147 	lock(&iomap);
148 	// find a free port above 0x400 and below 0x1000
149 	port = 0x400;
150 	for(l = &iomap.map; *l; l = &(*l)->next){
151 		map = *l;
152 		if (map->start < 0x400)
153 			continue;
154 		i = map->start - port;
155 		if(i > size)
156 			break;
157 		if(align > 0)
158 			port = ((port+align-1)/align)*align;
159 		else
160 			port = map->end;
161 	}
162 	if(*l == nil){
163 		unlock(&iomap);
164 		return -1;
165 	}
166 	map = iomap.free;
167 	if(map == nil){
168 		print("ioalloc: out of maps");
169 		unlock(&iomap);
170 		return port;
171 	}
172 	iomap.free = map->next;
173 	map->next = *l;
174 	map->start = port;
175 	map->end = port + size;
176 	map->reserved = 1;
177 	strncpy(map->tag, tag, sizeof(map->tag));
178 	map->tag[sizeof(map->tag)-1] = 0;
179 	*l = map;
180 
181 	archdir[0].qid.vers++;
182 
183 	unlock(&iomap);
184 	return map->start;
185 }
186 
187 //
188 //	alloc some io port space and remember who it was
189 //	alloced to.  if port < 0, find a free region.
190 //
191 int
ioalloc(int port,int size,int align,char * tag)192 ioalloc(int port, int size, int align, char *tag)
193 {
194 	IOMap *map, **l;
195 	int i;
196 
197 	lock(&iomap);
198 	if(port < 0){
199 		// find a free port above 0x400 and below 0x1000
200 		port = 0x400;
201 		for(l = &iomap.map; *l; l = &(*l)->next){
202 			map = *l;
203 			if (map->start < 0x400)
204 				continue;
205 			i = map->start - port;
206 			if(i > size)
207 				break;
208 			if(align > 0)
209 				port = ((port+align-1)/align)*align;
210 			else
211 				port = map->end;
212 		}
213 		if(*l == nil){
214 			unlock(&iomap);
215 			return -1;
216 		}
217 	} else {
218 		// Only 64KB I/O space on the x86.
219 		if((port+size) > 0x10000){
220 			unlock(&iomap);
221 			return -1;
222 		}
223 		// see if the space clashes with previously allocated ports
224 		for(l = &iomap.map; *l; l = &(*l)->next){
225 			map = *l;
226 			if(map->end <= port)
227 				continue;
228 			if(map->reserved && map->start == port && map->end == port + size) {
229 				map->reserved = 0;
230 				unlock(&iomap);
231 				return map->start;
232 			}
233 			if(map->start >= port+size)
234 				break;
235 			unlock(&iomap);
236 			return -1;
237 		}
238 	}
239 	map = iomap.free;
240 	if(map == nil){
241 		print("ioalloc: out of maps");
242 		unlock(&iomap);
243 		return port;
244 	}
245 	iomap.free = map->next;
246 	map->next = *l;
247 	map->start = port;
248 	map->end = port + size;
249 	strncpy(map->tag, tag, sizeof(map->tag));
250 	map->tag[sizeof(map->tag)-1] = 0;
251 	*l = map;
252 
253 	archdir[0].qid.vers++;
254 
255 	unlock(&iomap);
256 	return map->start;
257 }
258 
259 void
iofree(int port)260 iofree(int port)
261 {
262 	IOMap *map, **l;
263 
264 	lock(&iomap);
265 	for(l = &iomap.map; *l; l = &(*l)->next){
266 		if((*l)->start == port){
267 			map = *l;
268 			*l = map->next;
269 			map->next = iomap.free;
270 			iomap.free = map;
271 			break;
272 		}
273 		if((*l)->start > port)
274 			break;
275 	}
276 	archdir[0].qid.vers++;
277 	unlock(&iomap);
278 }
279 
280 int
iounused(int start,int end)281 iounused(int start, int end)
282 {
283 	IOMap *map;
284 
285 	for(map = iomap.map; map; map = map->next){
286 		if(start >= map->start && start < map->end
287 		|| start <= map->start && end > map->start)
288 			return 0;
289 	}
290 	return 1;
291 }
292 
293 static void
checkport(int start,int end)294 checkport(int start, int end)
295 {
296 	/* standard vga regs are OK */
297 	if(start >= 0x2b0 && end <= 0x2df+1)
298 		return;
299 	if(start >= 0x3c0 && end <= 0x3da+1)
300 		return;
301 
302 	if(iounused(start, end))
303 		return;
304 	error(Eperm);
305 }
306 
307 static Chan*
archattach(char * spec)308 archattach(char* spec)
309 {
310 	return devattach('P', spec);
311 }
312 
313 Walkqid*
archwalk(Chan * c,Chan * nc,char ** name,int nname)314 archwalk(Chan* c, Chan *nc, char** name, int nname)
315 {
316 	return devwalk(c, nc, name, nname, archdir, narchdir, devgen);
317 }
318 
319 static long
archstat(Chan * c,uchar * dp,long n)320 archstat(Chan* c, uchar* dp, long n)
321 {
322 	return devstat(c, dp, n, archdir, narchdir, devgen);
323 }
324 
325 static Chan*
archopen(Chan * c,int omode)326 archopen(Chan* c, int omode)
327 {
328 	return devopen(c, omode, archdir, narchdir, devgen);
329 }
330 
331 static void
archclose(Chan *)332 archclose(Chan*)
333 {
334 }
335 
336 enum
337 {
338 	Linelen= 31,
339 };
340 
341 static long
archread(Chan * c,void * a,long n,vlong offset)342 archread(Chan *c, void *a, long n, vlong offset)
343 {
344 	char *buf, *p;
345 	int port;
346 	ushort *sp;
347 	ulong *lp;
348 	IOMap *map;
349 	Rdwrfn *fn;
350 
351 	switch((ulong)c->qid.path){
352 
353 	case Qdir:
354 		return devdirread(c, a, n, archdir, narchdir, devgen);
355 
356 	case Qiob:
357 		port = offset;
358 		checkport(offset, offset+n);
359 		for(p = a; port < offset+n; port++)
360 			*p++ = inb(port);
361 		return n;
362 
363 	case Qiow:
364 		if(n & 1)
365 			error(Ebadarg);
366 		checkport(offset, offset+n);
367 		sp = a;
368 		for(port = offset; port < offset+n; port += 2)
369 			*sp++ = ins(port);
370 		return n;
371 
372 	case Qiol:
373 		if(n & 3)
374 			error(Ebadarg);
375 		checkport(offset, offset+n);
376 		lp = a;
377 		for(port = offset; port < offset+n; port += 4)
378 			*lp++ = inl(port);
379 		return n;
380 
381 	case Qioalloc:
382 		break;
383 
384 	default:
385 		if(c->qid.path < narchdir && (fn = readfn[c->qid.path]))
386 			return fn(c, a, n, offset);
387 		error(Eperm);
388 		break;
389 	}
390 
391 	if((buf = malloc(n)) == nil)
392 		error(Enomem);
393 	p = buf;
394 	n = n/Linelen;
395 	offset = offset/Linelen;
396 
397 	lock(&iomap);
398 	for(map = iomap.map; n > 0 && map != nil; map = map->next){
399 		if(offset-- > 0)
400 			continue;
401 		sprint(p, "%#8lux %#8lux %-12.12s\n", map->start, map->end-1, map->tag);
402 		p += Linelen;
403 		n--;
404 	}
405 	unlock(&iomap);
406 
407 	n = p - buf;
408 	memmove(a, buf, n);
409 	free(buf);
410 
411 	return n;
412 }
413 
414 static long
archwrite(Chan * c,void * a,long n,vlong offset)415 archwrite(Chan *c, void *a, long n, vlong offset)
416 {
417 	char *p;
418 	int port;
419 	ushort *sp;
420 	ulong *lp;
421 	Rdwrfn *fn;
422 
423 	switch((ulong)c->qid.path){
424 
425 	case Qiob:
426 		p = a;
427 		checkport(offset, offset+n);
428 		for(port = offset; port < offset+n; port++)
429 			outb(port, *p++);
430 		return n;
431 
432 	case Qiow:
433 		if(n & 1)
434 			error(Ebadarg);
435 		checkport(offset, offset+n);
436 		sp = a;
437 		for(port = offset; port < offset+n; port += 2)
438 			outs(port, *sp++);
439 		return n;
440 
441 	case Qiol:
442 		if(n & 3)
443 			error(Ebadarg);
444 		checkport(offset, offset+n);
445 		lp = a;
446 		for(port = offset; port < offset+n; port += 4)
447 			outl(port, *lp++);
448 		return n;
449 
450 	default:
451 		if(c->qid.path < narchdir && (fn = writefn[c->qid.path]))
452 			return fn(c, a, n, offset);
453 		error(Eperm);
454 		break;
455 	}
456 	return 0;
457 }
458 
459 Dev archdevtab = {
460 	'P',
461 	"arch",
462 
463 	devreset,
464 	devinit,
465 	devshutdown,
466 	archattach,
467 	archwalk,
468 	archstat,
469 	archopen,
470 	devcreate,
471 	archclose,
472 	archread,
473 	devbread,
474 	archwrite,
475 	devbwrite,
476 	devremove,
477 	devwstat,
478 };
479 
480 /*
481  */
482 void
nop(void)483 nop(void)
484 {
485 }
486 
487 void (*coherence)(void) = mfence;
488 
489 static long
cputyperead(Chan *,void * a,long n,vlong off)490 cputyperead(Chan*, void *a, long n, vlong off)
491 {
492 	char str[32];
493 
494 	snprint(str, sizeof(str), "%s %ud\n", "AMD64", m->cpumhz);
495 	return readstr(off, a, n, str);
496 }
497 
498 void
archinit(void)499 archinit(void)
500 {
501 	addarchfile("cputype", 0444, cputyperead, nil);
502 }
503 
504 void
archreset(void)505 archreset(void)
506 {
507 	int i;
508 
509 	/*
510 	 * And sometimes there is no keyboard...
511 	 *
512 	 * The reset register (0xcf9) is usually in one of the bridge
513 	 * chips. The actual location and sequence could be extracted from
514 	 * ACPI but why bother, this is the end of the line anyway.
515 	print("Takes a licking and keeps on ticking...\n");
516 	 */
517 	i = inb(0xcf9);					/* ICHx reset control */
518 	i &= 0x06;
519 	outb(0xcf9, i|0x02);				/* SYS_RST */
520 	millidelay(1);
521 	outb(0xcf9, i|0x06);				/* RST_CPU transition */
522 
523 	for(;;)
524 		pause();
525 }
526 
527 /*
528  *  return value and speed of timer
529  */
530 uvlong
fastticks(uvlong * hz)531 fastticks(uvlong* hz)
532 {
533 	if(hz != nil)
534 		*hz = m->cpuhz;
535 	return rdtsc();
536 }
537 
538 ulong
s(void)539 µs(void)
540 {
541 	return fastticks2us(rdtsc());
542 }
543 
544 /*
545  *  set next timer interrupt
546  */
547 void
timerset(uvlong x)548 timerset(uvlong x)
549 {
550 	extern void apictimerset(uvlong);
551 
552 	apictimerset(x);
553 }
554 
555 void
cycles(uvlong * t)556 cycles(uvlong* t)
557 {
558 	*t = rdtsc();
559 }
560 
561 void
delay(int millisecs)562 delay(int millisecs)
563 {
564 	u64int r, t;
565 
566 	if(millisecs <= 0)
567 		millisecs = 1;
568 	r = rdtsc();
569 	for(t = r + m->cpumhz*1000ull*millisecs; r < t; r = rdtsc())
570 		;
571 }
572 
573 /*
574  *  performance measurement ticks.  must be low overhead.
575  *  doesn't have to count over a second.
576  */
577 ulong
perfticks(void)578 perfticks(void)
579 {
580 	uvlong x;
581 
582 //	if(m->havetsc)
583 		cycles(&x);
584 //	else
585 //		x = 0;
586 	return x;
587 }
588