Plan 9 from Bell Labs’s /usr/web/sources/patch/applied/mtrrsupport/mtrr.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"

typedef struct Mtrreg Mtrreg;
typedef struct Mtrrop Mtrrop;

struct Mtrreg {
	vlong base;
	vlong mask;
};

struct Mtrrop {
	Mtrreg *reg;
	int slot;
};

enum {
	Uncacheable = 0,
	Writecomb = 1,
	Unknown1 = 2,
	Unknown2 = 3,
	Writethru = 4,
	Writeprot = 5,
	Writeback = 6
};

enum {
	Capvcnt = 0xff,
	Capwc = 1<<8,
	Capfix = 1<<10,
	Deftype = 0xff,
	Deffixena = 1<<10,
	Defena = 1<<11,
};

static char *types[] = {
[Uncacheable]	"uc",
[Writecomb]	"wc",
[Unknown1]	"uk1",
[Unknown2]	"uk2",
[Writethru]	"wt",
[Writeprot]	"wp",
[Writeback]	"wb",
			nil
};

static char *
type2str(int type)
{
	if(type < 0 || type >= nelem(types))
		return nil;
	return types[type];
}

static int
str2type(char *str)
{
	char **p;
	for(p = types; *p != nil; p++)
		if(!strcmp(str, *p))
			return p - types;
	return -1;
}

static vlong
physmask(void)
{
	ulong ax;
	cpuid(0x80000000, &ax, nil, nil, nil);
	if(ax < 0x80000008)
		return 0xFFFFFFFFFLL;	/* cpu does not tell. default to 36bit phys */
	cpuid(0x80000008, &ax, nil, nil, nil);
	return (1LL << (ax & 0xFF)) - 1;
}

static int
overlap(uintptr b1, long s1, uintptr b2, long s2)
{
	if(b1 > b2)
		return overlap(b2, s2, b1, s1);
	if(b1 + s1 > b2)
		return 1;
	return 0;
}

static int
ctpop(vlong a)
{	
	int i;
	i = 0;
	while(a > 0){
		if(a & 1)
			i++;
		a >>= 1;
	}
	return i;
}

static void
mtrrdec(Mtrreg *mtrr, uintptr *ptr, long *size, int *type, int *valid)
{
	if(ptr != nil) *ptr = mtrr->base & ~4095LL;
	if(type != nil) *type = mtrr->base & 0xff;
	if(size != nil) *size = (physmask() ^ (mtrr->mask & ~4095LL)) + 1;
	if(valid != nil) *valid = (mtrr->mask >> 11) & 1;
}

static void
mtrrenc(Mtrreg *mtrr, uintptr ptr, long size, int type, int valid)
{
	mtrr->base = ptr;
	mtrr->base |= type & 0xff;
	mtrr->mask = physmask() & ~(size - 1);
	mtrr->mask |= valid ? 1<<11 : 0;
}

static void
mtrrget(Mtrreg *mtrr, int i)
{
	rdmsr(0x200 + 2*i, &mtrr->base);
	rdmsr(0x200 + 2*i + 1, &mtrr->mask);
}

static void
mtrrput(Mtrreg *mtrr, int i)
{
	wrmsr(0x200 + 2*i, mtrr->base);
	wrmsr(0x200 + 2*i + 1, mtrr->mask);
}

static void
mtrrop(Mtrrop **op)
{
	vlong def;
	ulong cr0;
	ulong cr4;
	int pl;
	static long bar1, bar2;

	pl = splhi();

/*iprint("cpu%d enter mtrrop\n", m->machno);*/
	_xinc(&bar1);
	while(bar1 < conf.nmach)
		microdelay(10);

	cr4 = getcr4();
	putcr4(cr4 & ~(1<<7));	
	cr0 = getcr0();
	wbinvd();
	putcr0(cr0 | (1<<30));
	wbinvd();
	rdmsr(0x2FF, &def);
	wrmsr(0x2FF, def & ~(vlong)Defena);
	mtrrput((*op)->reg, (*op)->slot);
	wbinvd();
	wrmsr(0x2FF, def);
	putcr0(cr0);
	putcr4(cr4);

	_xinc(&bar2);
	while(bar2 < conf.nmach)
		microdelay(10);
	*op = nil;
	_xdec(&bar1);
	while(bar1 > 0)
		microdelay(10);
	_xdec(&bar2);
	splx(pl);
}

static Mtrrop *postedop;

void
mtrrcheck(void)
{
	if(postedop != nil)
		mtrrop(&postedop);
}

int
mtrr(uintptr base, long size, char *tstr)
{
	Mtrrop op;
	Mtrreg entry;
	vlong def, cap;
	int i, vcnt, slot, type, x;
	static int tickreg;
	static QLock mtrrlk;

	if((m->cpuiddx & Mtrr) == 0)
		error("mtrr not supported");

	if((base & 4095LL) != 0 || (size & 4095LL) != 0 || size <= 0)
		error("mtrr base and/or size not 4k aligned or size <= 0");

	if(base + size < base)
		error("mtrr range overlaps 4G");

	if(ctpop(size) != 1)
		error("mtrr size not power of 2");

	if((base & (size - 1)) != 0)
		error("mtrr base not naturally aligned");

	if((type = str2type(tstr)) == -1)
		error("mtrr invalid type");

	rdmsr(0x0FE, &cap);
	rdmsr(0x2FF, &def);

	switch(type){
	default:
		error("mtrr unknown type");
	case Writecomb:
		if((cap & Capwc) == 0)
			error("mtrr type wc (write combining) unsupported");
		/* fallthrough */
	case Uncacheable:
	case Writethru:
	case Writeprot:
	case Writeback:
		break;
	}

	slot = -1;
	vcnt = cap & Capvcnt;
	for(i = 0; i < vcnt; i++){
		Mtrreg mtrr;
		uintptr mp;
		long msize;
		int mtype, mvalid;
		mtrrget(&mtrr, i);
		mtrrdec(&mtrr, &mp, &msize, &mtype, &mvalid);
		if(!mvalid)
			slot = i;
		if(mvalid && mp == base && msize == size){
			slot = i;
			goto doit;
		}
		if(mvalid && overlap(mp, msize, base, size))
			error("mtrr range overlaps with existing definition");
	}
	if(slot == -1)
		error("no free mtrr slots");

doit:

	qlock(&mtrrlk);

	mtrrenc(&entry, base, size, type, 1);
	op.reg = &entry;
	op.slot = slot;
	postedop = &op;

	x = splhi();	/* avoid race with mtrrcheck */
	mtrrop(&postedop);
	splx(x);

	qunlock(&mtrrlk);

	return 0;
}

int
mtrrprint(char *buf, long bufsize)
{
	Mtrreg mtrr;
	vlong cap, def;
	long n;
	int i, vcnt;

	n = 0;
	if(m->cpuiddx & Mtrr){
		rdmsr(0x0FE, &cap);
		rdmsr(0x2FF, &def);
		n += snprint(buf+n, bufsize-n, "cache default %s\n", type2str(def & Deftype));
		vcnt = cap & Capvcnt;
		for(i = 0; i < vcnt; i++){
			uintptr base;
			long size;
			int type, valid;
			mtrrget(&mtrr, i);
			mtrrdec(&mtrr, &base, &size, &type, &valid);
			if(valid)
				n += snprint(buf+n, bufsize-n, "cache 0x%lux %lud %s\n", base, size, type2str(type));
		}
	}
	return n;
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].