450 lines · 7.2 KB · C
RawDownload
#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "../port/error.h" #include "ureg.h" typedef struct IOMap IOMap;struct IOMap{	IOMap	*next;	char	tag[13];	uint	start;	uint	end;}; static struct{	Lock;	IOMap	*map;	IOMap	*free;	IOMap	maps[32];		// some initial free maps 	QLock	ql;			// lock for reading map} iomap; enum {	Qdir = 0,	Qioalloc = 1,	Qiob,	Qiow,	Qiol,	Qbase, 	Qmax = 32,}; typedef long Rdwrfn(Chan*, void*, long, vlong); static Rdwrfn *readfn[Qmax];static Rdwrfn *writefn[Qmax]; static Dirtab archdir[Qmax] = {	".",		{ Qdir, 0, QTDIR },	0,	0555,	"ioalloc",	{ Qioalloc, 0 },	0,	0444,	"iob",		{ Qiob, 0 },		0,	0660,	"iow",		{ Qiow, 0 },		0,	0660,	"iol",		{ Qiol, 0 },		0,	0660,};Lock archwlock;	/* the lock is only for changing archdir */int narchdir = Qbase; /* * Add a file to the #P listing.  Once added, you can't delete it. * You can't add a file with the same name as one already there, * and you get a pointer to the Dirtab entry so you can do things * like change the Qid version.  Changing the Qid path is disallowed. */Dirtab*addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn){	int i;	Dirtab d;	Dirtab *dp; 	memset(&d, 0, sizeof d);	strcpy(d.name, name);	d.perm = perm; 	lock(&archwlock);	if(narchdir >= Qmax){		unlock(&archwlock);		return nil;	} 	for(i=0; i<narchdir; i++)		if(strcmp(archdir[i].name, name) == 0){			unlock(&archwlock);			return nil;		} 	d.qid.path = narchdir;	archdir[narchdir] = d;	readfn[narchdir] = rdfn;	writefn[narchdir] = wrfn;	dp = &archdir[narchdir++];	unlock(&archwlock); 	return dp;} voidioinit(void){	int i; 	for(i = 0; i < nelem(iomap.maps)-1; i++)		iomap.maps[i].next = &iomap.maps[i+1];	iomap.maps[i].next = nil;	iomap.free = iomap.maps;} /* * alloc some io port space and remember who it was * alloced to.  if port < 0, find a free region. */intioalloc(int port, int size, int /*align*/, char *tag){	IOMap *map, **l; 	lock(&iomap);	assert(port >= 0);	assert(port + size <= 0x10000);	/* 64k io space */	// see if the space clashes with previously allocated ports	for(l = &iomap.map; *l; l = &(*l)->next){		map = *l;		if(map->end <= port)			continue;		if(map->start >= port+size)			break;		unlock(&iomap);		return -1;	}	map = iomap.free;	if(map == nil)		panic("ioalloc: out of maps");	iomap.free = map->next;	map->next = *l;	map->start = port;	map->end = port + size;	strncpy(map->tag, tag, sizeof(map->tag));	map->tag[sizeof(map->tag)-1] = 0;	*l = map; 	archdir[0].qid.vers++; 	unlock(&iomap);	return map->start;} voidiofree(int port){	IOMap *map, **l; 	lock(&iomap);	for(l = &iomap.map; *l; l = &(*l)->next){		if((*l)->start == port){			map = *l;			*l = map->next;			map->next = iomap.free;			iomap.free = map;			break;		}		if((*l)->start > port)			break;	}	archdir[0].qid.vers++;	unlock(&iomap);} intiounused(int start, int end){	IOMap *map; 	for(map = iomap.map; map; map = map->next){		if(start >= map->start && start < map->end		|| start <= map->start && end > map->start)			return 0; 	}	return 1;} static voidcheckport(int start, int end){	/* standard vga regs are OK */	if(start >= 0x2b0 && end <= 0x2df+1)		return;	if(start >= 0x3c0 && end <= 0x3da+1)		return; 	if(iounused(start, end))		return;	error(Eperm);} static Chan*archattach(char* spec){	return devattach('P', spec);} Walkqid*archwalk(Chan* c, Chan *nc, char** name, int nname){	return devwalk(c, nc, name, nname, archdir, narchdir, devgen);} static longarchstat(Chan* c, uchar* dp, long n){	return devstat(c, dp, n, archdir, narchdir, devgen);} static Chan*archopen(Chan* c, int omode){	return devopen(c, omode, archdir, narchdir, devgen);} static voidarchclose(Chan*){} enum{	Linelen= 31,}; static longarchread(Chan *c, void *a, long n, vlong offset){	char *buf, *p;	int port;	u16int *sp;	u32int *lp;	IOMap *map;	Rdwrfn *fn; 	switch((uint)c->qid.path){ 	case Qdir:		return devdirread(c, a, n, archdir, narchdir, devgen); 	case Qiob:		port = offset;		checkport(offset, offset+n);		for(p = a; port < offset+n; port++)			*p++ = inb(port);		return n; 	case Qiow:		if(n & 1)			error(Ebadarg);		checkport(offset, offset+n);		sp = a;		for(port = offset; port < offset+n; port += 2)			*sp++ = ins(port);		return n; 	case Qiol:		if(n & 3)			error(Ebadarg);		checkport(offset, offset+n);		lp = a;		for(port = offset; port < offset+n; port += 4)			*lp++ = inl(port);		return n; 	case Qioalloc:		break; 	default:		if(c->qid.path < narchdir && (fn = readfn[c->qid.path]))			return fn(c, a, n, offset);		error(Eperm);		break;	} 	if((buf = malloc(n)) == nil)		error(Enomem);	p = buf;	n = n/Linelen;	offset = offset/Linelen; 	switch((uint)c->qid.path){	case Qioalloc:		lock(&iomap);		for(map = iomap.map; n > 0 && map != nil; map = map->next){			if(offset-- > 0)				continue;			sprint(p, "%#8ux %#8ux %-12.12s\n", map->start, map->end-1, map->tag);			p += Linelen;			n--;		}		unlock(&iomap);		break;	} 	n = p - buf;	memmove(a, buf, n);	free(buf); 	return n;} static longarchwrite(Chan *c, void *a, long n, vlong offset){	char *p;	int port;	u16int *sp;	u32int *lp;	Rdwrfn *fn; 	switch((ulong)c->qid.path){ 	case Qiob:		p = a;		checkport(offset, offset+n);		for(port = offset; port < offset+n; port++)			outb(port, *p++);		return n; 	case Qiow:		if(n & 1)			error(Ebadarg);		checkport(offset, offset+n);		sp = a;		for(port = offset; port < offset+n; port += 2)			outs(port, *sp++);		return n; 	case Qiol:		if(n & 3)			error(Ebadarg);		checkport(offset, offset+n);		lp = a;		for(port = offset; port < offset+n; port += 4)			outl(port, *lp++);		return n; 	default:		if(c->qid.path < narchdir && (fn = writefn[c->qid.path]))			return fn(c, a, n, offset);		error(Eperm);		break;	}	return 0;} Dev archdevtab = {	'P',	"arch", 	devreset,	devinit,	devshutdown,	archattach,	archwalk,	archstat,	archopen,	devcreate,	archclose,	archread,	devbread,	archwrite,	devbwrite,	devremove,	devwstat,}; static longcputyperead(Chan*, void *a, long n, vlong off){	char buf[512], *e; 	e = buf+sizeof buf;	seprint(buf, e, "%s %ud\n", "AMD64", m->cpumhz);	return readstr(off, a, n, buf);} voidarchinit(void){	addarchfile("cputype", 0444, cputyperead, nil);} voidarchreset(void){	int i; 	/*	 * And sometimes there is no keyboard...	 *	 * The reset register (0xcf9) is usually in one of the bridge	 * chips. The actual location and sequence could be extracted from	 * ACPI but why bother, this is the end of the line anyway.	print("Takes a licking and keeps on ticking...\n");	 */	i = inb(0xcf9);					/* ICHx reset control */	i &= 0x06;	outb(0xcf9, i|0x02);				/* SYS_RST */	delay(1);	outb(0xcf9, i|0x06);				/* RST_CPU transition */ 	for(;;)		;} /* *  return value and speed of timer */uvlongfastticks(uvlong* hz){	if(hz != nil)		*hz = m->cpuhz;	return rdtsc();} uintµs(void){	return fastticks2us(rdtsc());} /* *  set next timer interrupt */voidtimerset(uvlong x){	extern void lapictimerset(uvlong); 	lapictimerset(x);} voidcycles(uvlong* t){	*t = rdtsc();} /*   *  performance measurement ticks.  must be low overhead. *  doesn't have to count over a second. */uvlongperfticks(void){	return rdtsc();}