2012-06-17 21:12:19 +00:00
|
|
|
#include "u.h"
|
|
|
|
#include "../port/lib.h"
|
|
|
|
#include "mem.h"
|
|
|
|
#include "dat.h"
|
|
|
|
#include "fns.h"
|
|
|
|
#include "io.h"
|
2016-10-23 02:09:27 +00:00
|
|
|
#include "../port/error.h"
|
2012-06-17 21:12:19 +00:00
|
|
|
|
|
|
|
#include "mp.h"
|
|
|
|
|
|
|
|
#include <aml.h>
|
|
|
|
|
|
|
|
typedef struct Rsd Rsd;
|
|
|
|
typedef struct Tbl Tbl;
|
|
|
|
|
|
|
|
struct Rsd {
|
|
|
|
uchar sig[8];
|
|
|
|
uchar csum;
|
|
|
|
uchar oemid[6];
|
|
|
|
uchar rev;
|
|
|
|
uchar raddr[4];
|
|
|
|
uchar len[4];
|
|
|
|
uchar xaddr[8];
|
|
|
|
uchar xcsum;
|
|
|
|
uchar reserved[3];
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Tbl {
|
|
|
|
uchar sig[4];
|
|
|
|
uchar len[4];
|
|
|
|
uchar rev;
|
|
|
|
uchar csum;
|
|
|
|
uchar oemid[6];
|
|
|
|
uchar oemtid[8];
|
|
|
|
uchar oemrev[4];
|
|
|
|
uchar cid[4];
|
|
|
|
uchar crev[4];
|
|
|
|
uchar data[];
|
|
|
|
};
|
|
|
|
|
2013-06-19 20:26:27 +00:00
|
|
|
enum {
|
|
|
|
Tblsz = 4+4+1+1+6+8+4+4+4,
|
|
|
|
};
|
|
|
|
|
2012-06-17 21:12:19 +00:00
|
|
|
static Rsd *rsd;
|
2013-06-07 16:03:44 +00:00
|
|
|
|
|
|
|
/* physical addresses visited by maptable() */
|
|
|
|
static int ntblpa;
|
|
|
|
static uintptr tblpa[64];
|
|
|
|
|
|
|
|
/* successfully mapped tables */
|
|
|
|
static int ntblmap;
|
|
|
|
static Tbl *tblmap[64];
|
2012-06-17 21:12:19 +00:00
|
|
|
|
|
|
|
static ushort
|
|
|
|
get16(uchar *p){
|
|
|
|
return p[1]<<8 | p[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint
|
|
|
|
get32(uchar *p){
|
|
|
|
return p[3]<<24 | p[2]<<16 | p[1]<<8 | p[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
static uvlong
|
|
|
|
get64(uchar *p){
|
|
|
|
uvlong u;
|
|
|
|
|
|
|
|
u = get32(p+4);
|
|
|
|
return u<<32 | get32(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint
|
|
|
|
tbldlen(Tbl *t){
|
2013-06-19 20:26:27 +00:00
|
|
|
return get32(t->len) - Tblsz;
|
2012-06-17 21:12:19 +00:00
|
|
|
}
|
|
|
|
|
2016-10-23 02:09:27 +00:00
|
|
|
static long
|
|
|
|
memcheck(uintptr pa, long len)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
uintptr pe;
|
|
|
|
Confmem *cm;
|
|
|
|
|
|
|
|
if(len <= 0)
|
|
|
|
return len;
|
|
|
|
pe = pa + len-1;
|
|
|
|
if(pe < pa){
|
|
|
|
len = -pa;
|
|
|
|
pe = pa + len-1;
|
|
|
|
}
|
|
|
|
if(pa < PADDR(CPU0END))
|
|
|
|
return 0;
|
|
|
|
if(pe >= PADDR(KTZERO) && pa < PADDR(end))
|
|
|
|
return PADDR(KTZERO) - pa;
|
|
|
|
for(i=0; i<nelem(conf.mem); i++){
|
|
|
|
cm = &conf.mem[i];
|
|
|
|
if(cm->npage == 0)
|
|
|
|
continue;
|
|
|
|
if(pe >= cm->base && pa <= cm->base + cm->npage*BY2PG - 1)
|
|
|
|
return cm->base - pa;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2012-06-17 21:12:19 +00:00
|
|
|
static void
|
|
|
|
maptable(uvlong xpa)
|
|
|
|
{
|
|
|
|
uchar *p, *e;
|
|
|
|
uintptr pa;
|
|
|
|
u32int l;
|
|
|
|
Tbl *t;
|
2013-06-07 16:03:44 +00:00
|
|
|
int i;
|
2012-06-17 21:12:19 +00:00
|
|
|
|
|
|
|
pa = xpa;
|
|
|
|
if((uvlong)pa != xpa || pa == 0)
|
|
|
|
return;
|
2013-06-07 16:03:44 +00:00
|
|
|
if(ntblpa >= nelem(tblpa) || ntblmap >= nelem(tblmap))
|
2012-06-17 21:12:19 +00:00
|
|
|
return;
|
2013-06-07 16:03:44 +00:00
|
|
|
|
|
|
|
for(i=0; i<ntblpa; i++){
|
|
|
|
if(pa == tblpa[i])
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
tblpa[ntblpa++] = pa;
|
|
|
|
|
2012-06-17 21:12:19 +00:00
|
|
|
if((t = vmap(pa, 8)) == nil)
|
|
|
|
return;
|
|
|
|
l = get32(t->len);
|
2013-06-19 20:26:27 +00:00
|
|
|
if(l < Tblsz){
|
2012-06-17 21:12:19 +00:00
|
|
|
vunmap(t, 8);
|
|
|
|
return;
|
|
|
|
}
|
2016-10-23 02:09:27 +00:00
|
|
|
if(memcheck(pa, l) != l){
|
|
|
|
print("maptable: ignoring %.4s at [%#p-%#p); overlaps usable memory\n",
|
|
|
|
(char*)t->sig, pa, pa+l);
|
|
|
|
vunmap(t, 8);
|
|
|
|
return;
|
|
|
|
}
|
2012-06-17 21:12:19 +00:00
|
|
|
vunmap(t, 8);
|
2016-10-23 02:09:27 +00:00
|
|
|
|
2012-06-17 21:12:19 +00:00
|
|
|
if((t = vmap(pa, l)) == nil)
|
|
|
|
return;
|
|
|
|
if(checksum(t, l)){
|
|
|
|
vunmap(t, l);
|
|
|
|
return;
|
|
|
|
}
|
2013-06-07 16:03:44 +00:00
|
|
|
tblmap[ntblmap++] = t;
|
2012-06-17 21:12:19 +00:00
|
|
|
|
|
|
|
p = (uchar*)t;
|
|
|
|
e = p + l;
|
|
|
|
if(memcmp("RSDT", t->sig, 4) == 0){
|
|
|
|
for(p = t->data; p+3 < e; p += 4)
|
|
|
|
maptable(get32(p));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(memcmp("XSDT", t->sig, 4) == 0){
|
|
|
|
for(p = t->data; p+7 < e; p += 8)
|
|
|
|
maptable(get64(p));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(memcmp("FACP", t->sig, 4) == 0){
|
|
|
|
if(l < 44)
|
|
|
|
return;
|
|
|
|
maptable(get32(p + 40));
|
|
|
|
if(l < 148)
|
|
|
|
return;
|
|
|
|
maptable(get64(p + 140));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
maptables(void)
|
|
|
|
{
|
2013-06-07 16:03:44 +00:00
|
|
|
if(rsd == nil || ntblmap > 0 || ntblpa > 0)
|
2012-06-17 21:12:19 +00:00
|
|
|
return;
|
|
|
|
if(!checksum(rsd, 20))
|
|
|
|
maptable(get32(rsd->raddr));
|
|
|
|
if(rsd->rev >= 2)
|
|
|
|
if(!checksum(rsd, 36))
|
|
|
|
maptable(get64(rsd->xaddr));
|
|
|
|
}
|
|
|
|
|
|
|
|
static Apic*
|
|
|
|
findapic(int gsi, int *pintin)
|
|
|
|
{
|
|
|
|
Apic *a;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for(i=0; i<=MaxAPICNO; i++){
|
|
|
|
if((a = mpioapic[i]) == nil)
|
|
|
|
continue;
|
|
|
|
if((a->flags & PcmpEN) == 0)
|
|
|
|
continue;
|
2012-06-16 23:06:57 +00:00
|
|
|
if(gsi >= a->gsibase && gsi <= a->gsibase+a->mre){
|
2012-06-17 21:12:19 +00:00
|
|
|
if(pintin)
|
|
|
|
*pintin = gsi - a->gsibase;
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
print("findapic: no ioapic found for gsi %d\n", gsi);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
addirq(int gsi, int type, int busno, int irq, int flags)
|
|
|
|
{
|
|
|
|
Apic *a;
|
|
|
|
Bus *bus;
|
|
|
|
Aintr *ai;
|
|
|
|
PCMPintr *pi;
|
|
|
|
int intin;
|
|
|
|
|
|
|
|
if((a = findapic(gsi, &intin)) == nil)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for(bus = mpbus; bus; bus = bus->next)
|
|
|
|
if(bus->type == type && bus->busno == busno)
|
|
|
|
goto Foundbus;
|
|
|
|
|
|
|
|
if((bus = xalloc(sizeof(Bus))) == nil)
|
|
|
|
panic("addirq: no memory for Bus");
|
|
|
|
bus->busno = busno;
|
|
|
|
bus->type = type;
|
|
|
|
if(type == BusISA){
|
|
|
|
bus->po = PcmpHIGH;
|
|
|
|
bus->el = PcmpEDGE;
|
|
|
|
if(mpisabus == -1)
|
|
|
|
mpisabus = busno;
|
|
|
|
} else {
|
|
|
|
bus->po = PcmpLOW;
|
|
|
|
bus->el = PcmpLEVEL;
|
|
|
|
}
|
|
|
|
if(mpbus)
|
|
|
|
mpbuslast->next = bus;
|
|
|
|
else
|
|
|
|
mpbus = bus;
|
|
|
|
mpbuslast = bus;
|
|
|
|
|
|
|
|
Foundbus:
|
|
|
|
for(ai = bus->aintr; ai; ai = ai->next)
|
|
|
|
if(ai->intr->irq == irq)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if((pi = xalloc(sizeof(PCMPintr))) == nil)
|
|
|
|
panic("addirq: no memory for PCMPintr");
|
|
|
|
pi->type = PcmpIOINTR;
|
|
|
|
pi->intr = PcmpINT;
|
|
|
|
pi->flags = flags & (PcmpPOMASK|PcmpELMASK);
|
|
|
|
pi->busno = busno;
|
|
|
|
pi->irq = irq;
|
|
|
|
pi->apicno = a->apicno;
|
|
|
|
pi->intin = intin;
|
|
|
|
|
|
|
|
if((ai = xalloc(sizeof(Aintr))) == nil)
|
|
|
|
panic("addirq: no memory for Aintr");
|
|
|
|
ai->intr = pi;
|
|
|
|
ai->apic = a;
|
|
|
|
ai->next = bus->aintr;
|
|
|
|
bus->aintr = ai;
|
|
|
|
}
|
|
|
|
|
2012-06-19 14:30:11 +00:00
|
|
|
static char*
|
|
|
|
eisaid(void *v)
|
|
|
|
{
|
|
|
|
static char id[8];
|
|
|
|
ulong b, l;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if(amltag(v) == 's')
|
|
|
|
return v;
|
|
|
|
b = amlint(v);
|
|
|
|
for(l = 0, i=24; i>=0; i -= 8, b >>= 8)
|
|
|
|
l |= (b & 0xFF) << i;
|
|
|
|
id[7] = 0;
|
|
|
|
for(i=6; i>=3; i--, l >>= 4)
|
|
|
|
id[i] = "0123456789ABCDEF"[l & 0xF];
|
|
|
|
for(i=2; i>=0; i--, l >>= 5)
|
|
|
|
id[i] = '@' + (l & 0x1F);
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2012-06-17 21:12:19 +00:00
|
|
|
static int
|
|
|
|
pcibusno(void *dot)
|
|
|
|
{
|
|
|
|
int bno, adr, tbdf;
|
|
|
|
Pcidev *pdev;
|
|
|
|
void *p, *x;
|
2012-06-19 14:30:11 +00:00
|
|
|
char *id;
|
2012-06-17 21:12:19 +00:00
|
|
|
|
2012-06-19 14:30:11 +00:00
|
|
|
id = nil;
|
2013-09-06 14:50:43 +00:00
|
|
|
if((x = amlwalk(dot, "^_HID")) != nil)
|
|
|
|
if((p = amlval(x)) != nil)
|
2012-06-19 14:30:11 +00:00
|
|
|
id = eisaid(p);
|
|
|
|
if((x = amlwalk(dot, "^_BBN")) == nil)
|
|
|
|
if((x = amlwalk(dot, "^_ADR")) == nil)
|
|
|
|
return -1;
|
2016-06-30 17:11:06 +00:00
|
|
|
p = nil;
|
|
|
|
if(amleval(x, "", &p) < 0)
|
|
|
|
return -1;
|
|
|
|
adr = amlint(p);
|
2012-06-19 14:30:11 +00:00
|
|
|
/* if root bridge, then we are done here */
|
2013-09-06 14:50:43 +00:00
|
|
|
if(id != nil && (strcmp(id, "PNP0A03")==0 || strcmp(id, "PNP0A08")==0))
|
2012-06-19 14:30:11 +00:00
|
|
|
return adr;
|
2012-06-17 21:12:19 +00:00
|
|
|
x = amlwalk(dot, "^");
|
|
|
|
if(x == nil || x == dot)
|
|
|
|
return -1;
|
|
|
|
if((bno = pcibusno(x)) < 0)
|
|
|
|
return -1;
|
|
|
|
tbdf = MKBUS(BusPCI, bno, adr>>16, adr&0xFFFF);
|
|
|
|
pdev = pcimatchtbdf(tbdf);
|
2013-09-08 00:33:25 +00:00
|
|
|
if(pdev == nil)
|
2012-06-17 21:12:19 +00:00
|
|
|
return -1;
|
2013-09-08 00:33:25 +00:00
|
|
|
if(pdev->bridge == nil)
|
|
|
|
return bno;
|
2012-06-17 21:12:19 +00:00
|
|
|
return BUSBNO(pdev->bridge->tbdf);
|
|
|
|
}
|
|
|
|
|
2013-09-06 14:50:43 +00:00
|
|
|
static int
|
|
|
|
pciaddr(void *dot)
|
|
|
|
{
|
|
|
|
int adr, bno;
|
2016-06-30 17:11:06 +00:00
|
|
|
void *x, *p;
|
2013-09-06 14:50:43 +00:00
|
|
|
|
|
|
|
for(;;){
|
|
|
|
if((x = amlwalk(dot, "_ADR")) == nil){
|
|
|
|
x = amlwalk(dot, "^");
|
|
|
|
if(x == nil || x == dot)
|
|
|
|
break;
|
|
|
|
dot = x;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if((bno = pcibusno(x)) < 0)
|
|
|
|
break;
|
2016-06-30 17:11:06 +00:00
|
|
|
p = nil;
|
|
|
|
if(amleval(x, "", &p) < 0)
|
2013-09-06 14:50:43 +00:00
|
|
|
break;
|
2016-06-30 17:11:06 +00:00
|
|
|
adr = amlint(p);
|
2013-09-06 14:50:43 +00:00
|
|
|
return MKBUS(BusPCI, bno, adr>>16, adr&0xFFFF);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
getirqs(void *d, uchar pmask[32], int *pflags)
|
|
|
|
{
|
|
|
|
int i, n, m;
|
|
|
|
uchar *p;
|
|
|
|
|
|
|
|
*pflags = 0;
|
|
|
|
memset(pmask, 0, 32);
|
|
|
|
if(amltag(d) != 'b')
|
|
|
|
return -1;
|
|
|
|
p = amlval(d);
|
|
|
|
if(amllen(d) >= 2 && (p[0] == 0x22 || p[0] == 0x23)){
|
|
|
|
pmask[0] = p[1];
|
|
|
|
pmask[1] = p[2];
|
|
|
|
if(amllen(d) >= 3 && p[0] == 0x23)
|
|
|
|
*pflags = ((p[3] & (1<<0)) ? PcmpEDGE : PcmpLEVEL)
|
|
|
|
| ((p[3] & (1<<3)) ? PcmpLOW : PcmpHIGH);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if(amllen(d) >= 5 && p[0] == 0x89){
|
|
|
|
n = p[4];
|
|
|
|
if(amllen(d) < 5+n*4)
|
|
|
|
return -1;
|
|
|
|
for(i=0; i<n; i++){
|
|
|
|
m = get32(p+5 + i*4);
|
|
|
|
if(m >= 0 && m < 256)
|
|
|
|
pmask[m/8] |= 1<<(m%8);
|
|
|
|
}
|
|
|
|
*pflags = ((p[3] & (1<<1)) ? PcmpEDGE : PcmpLEVEL)
|
|
|
|
| ((p[3] & (1<<2)) ? PcmpLOW : PcmpHIGH);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uchar*
|
|
|
|
setirq(void *d, uint irq)
|
|
|
|
{
|
|
|
|
uchar *p;
|
|
|
|
|
|
|
|
if(amltag(d) != 'b')
|
|
|
|
return nil;
|
|
|
|
p = amlnew('b', amllen(d));
|
|
|
|
memmove(p, d, amllen(p));
|
|
|
|
if(p[0] == 0x22 || p[0] == 0x23){
|
|
|
|
irq = 1<<irq;
|
|
|
|
p[1] = irq;
|
|
|
|
p[2] = irq>>8;
|
|
|
|
}
|
|
|
|
if(p[0] == 0x89){
|
|
|
|
p[4] = 1;
|
|
|
|
p[5] = irq;
|
|
|
|
p[6] = irq>>8;
|
|
|
|
p[7] = irq>>16;
|
|
|
|
p[8] = irq>>24;
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
setuplink(void *link, int *pflags)
|
|
|
|
{
|
|
|
|
uchar im, pm[32], cm[32], *c;
|
2013-09-08 00:33:25 +00:00
|
|
|
static int lastirq = 1;
|
|
|
|
int gsi, i;
|
2013-09-06 14:50:43 +00:00
|
|
|
void *r;
|
|
|
|
|
|
|
|
if(amltag(link) != 'N')
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
r = nil;
|
|
|
|
if(amleval(amlwalk(link, "_PRS"), "", &r) < 0)
|
|
|
|
return -1;
|
|
|
|
if(getirqs(r, pm, pflags) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
r = nil;
|
|
|
|
if(amleval(amlwalk(link, "_CRS"), "", &r) < 0)
|
|
|
|
return -1;
|
|
|
|
if(getirqs(r, cm, pflags) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2013-09-08 00:33:25 +00:00
|
|
|
gsi = -1;
|
2013-09-06 14:50:43 +00:00
|
|
|
for(i=0; i<256; i++){
|
|
|
|
im = 1<<(i%8);
|
|
|
|
if(pm[i/8] & im){
|
2013-09-08 00:33:25 +00:00
|
|
|
if(cm[i/8] & im)
|
2013-09-06 14:50:43 +00:00
|
|
|
gsi = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-08 00:33:25 +00:00
|
|
|
if(gsi > 0 || getconf("*nopcirouting") != nil)
|
|
|
|
return gsi;
|
|
|
|
|
|
|
|
for(i=0; i<256; i++){
|
|
|
|
gsi = lastirq++ & 0xFF; /* round robin */
|
|
|
|
im = 1<<(gsi%8);
|
|
|
|
if(pm[gsi/8] & im){
|
|
|
|
if((c = setirq(r, gsi)) == nil)
|
|
|
|
break;
|
|
|
|
if(amleval(amlwalk(link, "_SRS"), "b", c, nil) < 0)
|
|
|
|
break;
|
|
|
|
return gsi;
|
2013-09-06 14:50:43 +00:00
|
|
|
}
|
|
|
|
}
|
2013-09-08 00:33:25 +00:00
|
|
|
return -1;
|
2013-09-06 14:50:43 +00:00
|
|
|
}
|
|
|
|
|
2012-06-17 21:12:19 +00:00
|
|
|
static int
|
|
|
|
enumprt(void *dot, void *)
|
|
|
|
{
|
|
|
|
void *p, **a, **b;
|
2013-09-06 14:50:43 +00:00
|
|
|
int bno, dno, pin, gsi, flags;
|
2012-06-17 21:12:19 +00:00
|
|
|
int n, i;
|
|
|
|
|
|
|
|
bno = pcibusno(dot);
|
2013-01-29 17:24:48 +00:00
|
|
|
if(bno < 0)
|
2012-06-17 21:12:19 +00:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* evalulate _PRT method */
|
|
|
|
p = nil;
|
|
|
|
if(amleval(dot, "", &p) < 0)
|
|
|
|
return 1;
|
|
|
|
if(amltag(p) != 'p')
|
|
|
|
return 1;
|
|
|
|
|
2013-09-06 14:50:43 +00:00
|
|
|
amltake(p);
|
2012-06-17 21:12:19 +00:00
|
|
|
n = amllen(p);
|
|
|
|
a = amlval(p);
|
|
|
|
for(i=0; i<n; i++){
|
|
|
|
if(amltag(a[i]) != 'p')
|
|
|
|
continue;
|
|
|
|
if(amllen(a[i]) != 4)
|
|
|
|
continue;
|
2013-09-06 14:50:43 +00:00
|
|
|
flags = 0;
|
2012-06-17 21:12:19 +00:00
|
|
|
b = amlval(a[i]);
|
|
|
|
dno = amlint(b[0])>>16;
|
|
|
|
pin = amlint(b[1]);
|
2013-09-06 14:50:43 +00:00
|
|
|
gsi = amlint(b[3]);
|
|
|
|
if(gsi==0){
|
|
|
|
gsi = setuplink(b[2], &flags);
|
|
|
|
if(gsi <= 0)
|
|
|
|
continue;
|
2012-06-17 21:12:19 +00:00
|
|
|
}
|
2013-09-06 14:50:43 +00:00
|
|
|
addirq(gsi, BusPCI, bno, (dno<<2)|pin, flags);
|
2012-06-17 21:12:19 +00:00
|
|
|
}
|
2013-09-06 14:50:43 +00:00
|
|
|
amldrop(p);
|
|
|
|
|
2012-06-17 21:12:19 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-11-09 23:04:37 +00:00
|
|
|
static int
|
|
|
|
enumec(void *dot, void *)
|
|
|
|
{
|
|
|
|
int cmdport, dataport;
|
|
|
|
uchar *b;
|
|
|
|
void *x;
|
|
|
|
char *id;
|
|
|
|
|
|
|
|
b = nil;
|
|
|
|
id = eisaid(amlval(amlwalk(dot, "^_HID")));
|
|
|
|
if(id == nil || strcmp(id, "PNP0C09") != 0)
|
|
|
|
return 1;
|
|
|
|
if((x = amlwalk(dot, "^_CRS")) == nil)
|
|
|
|
return 1;
|
|
|
|
if(amleval(x, "", &b) < 0 || amltag(b) != 'b' || amllen(b) < 16)
|
|
|
|
return 1;
|
|
|
|
if(b[0] != 0x47 || b[8] != 0x47) /* two i/o port descriptors */
|
|
|
|
return 1;
|
|
|
|
dataport = b[0+2] | b[0+3]<<8;
|
|
|
|
cmdport = b[8+2] | b[8+3]<<8;
|
|
|
|
ecinit(cmdport, dataport);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-10-18 21:04:19 +00:00
|
|
|
static long
|
|
|
|
readmem(Chan*, void *v, long n, vlong o)
|
|
|
|
{
|
2016-10-23 02:09:27 +00:00
|
|
|
uintptr pa = (uintptr)o;
|
|
|
|
void *t;
|
|
|
|
|
|
|
|
if((n = memcheck(pa, n)) <= 0)
|
|
|
|
return 0;
|
|
|
|
if((t = vmap(pa, n)) == nil)
|
|
|
|
error(Enovmem);
|
|
|
|
if(waserror()){
|
|
|
|
vunmap(t, n);
|
|
|
|
nexterror();
|
|
|
|
}
|
|
|
|
memmove(v, t, n);
|
|
|
|
vunmap(t, n);
|
|
|
|
poperror();
|
|
|
|
return n;
|
2016-10-18 21:04:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
writemem(Chan*, void *v, long n, vlong o)
|
|
|
|
{
|
2016-10-23 02:09:27 +00:00
|
|
|
uintptr pa = (uintptr)o;
|
|
|
|
void *t;
|
|
|
|
|
|
|
|
if(memcheck(pa, n) != n)
|
|
|
|
error(Eio);
|
|
|
|
if((t = vmap(pa, n)) == nil)
|
|
|
|
error(Enovmem);
|
|
|
|
if(waserror()){
|
|
|
|
vunmap(t, n);
|
|
|
|
nexterror();
|
|
|
|
}
|
|
|
|
memmove(t, v, n);
|
|
|
|
vunmap(t, n);
|
|
|
|
poperror();
|
|
|
|
return n;
|
2016-10-18 21:04:19 +00:00
|
|
|
}
|
|
|
|
|
2012-06-17 21:12:19 +00:00
|
|
|
static void
|
|
|
|
acpiinit(void)
|
|
|
|
{
|
|
|
|
Tbl *t;
|
|
|
|
Apic *a;
|
|
|
|
void *va;
|
2014-04-08 17:35:29 +00:00
|
|
|
uchar *s, *p, *e;
|
2012-06-17 21:12:19 +00:00
|
|
|
ulong lapicbase;
|
|
|
|
int machno, i, c;
|
|
|
|
|
|
|
|
maptables();
|
|
|
|
|
|
|
|
amlinit();
|
|
|
|
|
2013-06-07 16:03:44 +00:00
|
|
|
/* load DSDT */
|
|
|
|
for(i=0; i<ntblmap; i++){
|
|
|
|
t = tblmap[i];
|
|
|
|
if(memcmp(t->sig, "DSDT", 4) == 0){
|
2016-06-05 12:57:38 +00:00
|
|
|
amlintmask = (~0ULL) >> (t->rev <= 1)*32;
|
2013-06-07 16:03:44 +00:00
|
|
|
amlload(t->data, tbldlen(t));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* load SSDT, there can be multiple tables */
|
|
|
|
for(i=0; i<ntblmap; i++){
|
|
|
|
t = tblmap[i];
|
|
|
|
if(memcmp(t->sig, "SSDT", 4) == 0)
|
|
|
|
amlload(t->data, tbldlen(t));
|
|
|
|
}
|
2012-06-17 21:12:19 +00:00
|
|
|
|
|
|
|
/* set APIC mode */
|
|
|
|
amleval(amlwalk(amlroot, "_PIC"), "i", 1, nil);
|
|
|
|
|
2013-06-07 16:03:44 +00:00
|
|
|
for(i=0; i<ntblmap; i++){
|
|
|
|
t = tblmap[i];
|
|
|
|
if(memcmp(t->sig, "APIC", 4) == 0)
|
|
|
|
goto Foundapic;
|
|
|
|
}
|
|
|
|
panic("acpiinit: no MADT (APIC) table");
|
|
|
|
return;
|
2012-06-17 21:12:19 +00:00
|
|
|
|
2013-06-07 16:03:44 +00:00
|
|
|
Foundapic:
|
2014-04-08 17:35:29 +00:00
|
|
|
s = t->data;
|
|
|
|
e = s + tbldlen(t);
|
|
|
|
lapicbase = get32(s); s += 8;
|
2012-06-17 21:12:19 +00:00
|
|
|
va = vmap(lapicbase, 1024);
|
2014-02-01 09:23:17 +00:00
|
|
|
print("LAPIC: %.8lux %#p\n", lapicbase, va);
|
2012-06-17 21:12:19 +00:00
|
|
|
if(va == nil)
|
|
|
|
panic("acpiinit: cannot map lapic %.8lux", lapicbase);
|
|
|
|
|
|
|
|
machno = 0;
|
2014-04-08 17:35:29 +00:00
|
|
|
for(p = s; p < e; p += c){
|
2012-06-17 21:12:19 +00:00
|
|
|
c = p[1];
|
|
|
|
if(c < 2 || (p+c) > e)
|
|
|
|
break;
|
|
|
|
switch(*p){
|
|
|
|
case 0x00: /* Processor Local APIC */
|
|
|
|
if(p[3] > MaxAPICNO)
|
|
|
|
break;
|
|
|
|
if((a = xalloc(sizeof(Apic))) == nil)
|
|
|
|
panic("acpiinit: no memory for Apic");
|
|
|
|
a->type = PcmpPROCESSOR;
|
|
|
|
a->apicno = p[3];
|
|
|
|
a->paddr = lapicbase;
|
|
|
|
a->addr = va;
|
|
|
|
a->lintr[0] = ApicIMASK;
|
|
|
|
a->lintr[1] = ApicIMASK;
|
2013-06-19 20:26:27 +00:00
|
|
|
a->flags = p[4] & PcmpEN;
|
2013-07-07 10:44:30 +00:00
|
|
|
|
|
|
|
/* skip disabled processors */
|
|
|
|
if((a->flags & PcmpEN) == 0 || mpapic[a->apicno] != nil){
|
|
|
|
xfree(a);
|
|
|
|
break;
|
2012-06-17 21:12:19 +00:00
|
|
|
}
|
2013-07-07 10:44:30 +00:00
|
|
|
a->machno = machno++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* platform firmware should list the boot processor
|
|
|
|
* as the first processor entry in the MADT
|
|
|
|
*/
|
|
|
|
if(a->machno == 0)
|
|
|
|
a->flags |= PcmpBP;
|
|
|
|
|
2012-06-17 21:12:19 +00:00
|
|
|
mpapic[a->apicno] = a;
|
|
|
|
break;
|
|
|
|
case 0x01: /* I/O APIC */
|
|
|
|
if(p[2] > MaxAPICNO)
|
|
|
|
break;
|
|
|
|
if((a = xalloc(sizeof(Apic))) == nil)
|
|
|
|
panic("acpiinit: no memory for io Apic");
|
|
|
|
a->type = PcmpIOAPIC;
|
|
|
|
a->apicno = p[2];
|
|
|
|
a->paddr = get32(p+4);
|
|
|
|
if((a->addr = vmap(a->paddr, 1024)) == nil)
|
|
|
|
panic("acpiinit: cannot map ioapic %.8lux", a->paddr);
|
|
|
|
a->gsibase = get32(p+8);
|
|
|
|
a->flags = PcmpEN;
|
|
|
|
mpioapic[a->apicno] = a;
|
|
|
|
ioapicinit(a, a->apicno);
|
|
|
|
break;
|
2014-04-08 17:35:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* need 2nd pass as vbox puts interrupt overrides
|
|
|
|
* *before* the ioapic entries (!)
|
|
|
|
*/
|
|
|
|
for(p = s; p < e; p += c){
|
|
|
|
c = p[1];
|
|
|
|
if(c < 2 || (p+c) > e)
|
|
|
|
break;
|
|
|
|
switch(*p){
|
2012-06-17 21:12:19 +00:00
|
|
|
case 0x02: /* Interrupt Source Override */
|
|
|
|
addirq(get32(p+4), BusISA, 0, p[3], get16(p+8));
|
|
|
|
break;
|
|
|
|
case 0x03: /* NMI Source */
|
|
|
|
case 0x04: /* Local APIC NMI */
|
|
|
|
case 0x05: /* Local APIC Address Override */
|
|
|
|
case 0x06: /* I/O SAPIC */
|
|
|
|
case 0x07: /* Local SAPIC */
|
|
|
|
case 0x08: /* Platform Interrupt Sources */
|
|
|
|
case 0x09: /* Processor Local x2APIC */
|
|
|
|
case 0x0A: /* x2APIC NMI */
|
|
|
|
case 0x0B: /* GIC */
|
|
|
|
case 0x0C: /* GICD */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-18 21:04:19 +00:00
|
|
|
/* find embedded controller */
|
|
|
|
amlenum(amlroot, "_HID", enumec, nil);
|
|
|
|
|
2012-06-17 21:12:19 +00:00
|
|
|
/* look for PCI interrupt mappings */
|
|
|
|
amlenum(amlroot, "_PRT", enumprt, nil);
|
|
|
|
|
|
|
|
/* add identity mapped legacy isa interrupts */
|
|
|
|
for(i=0; i<16; i++)
|
|
|
|
addirq(i, BusISA, 0, i, 0);
|
|
|
|
|
|
|
|
/* free the AML interpreter */
|
|
|
|
amlexit();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ininitalize local APIC and start application processors.
|
|
|
|
*/
|
|
|
|
mpinit();
|
|
|
|
}
|
|
|
|
|
2014-12-19 22:34:43 +00:00
|
|
|
static void
|
|
|
|
acpireset(void)
|
|
|
|
{
|
|
|
|
uchar *p;
|
|
|
|
Tbl *t;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* stop application processors */
|
|
|
|
mpshutdown();
|
|
|
|
|
|
|
|
/* locate and write platform reset register */
|
|
|
|
for(i=0; i < ntblmap; i++){
|
|
|
|
t = tblmap[i];
|
|
|
|
if(memcmp(t->sig, "FACP", 4) != 0)
|
|
|
|
continue;
|
|
|
|
if(get32(t->len) <= 128)
|
|
|
|
break;
|
|
|
|
p = (uchar*)t;
|
|
|
|
if((get32(p + 112) & (1<<10)) == 0)
|
|
|
|
break;
|
|
|
|
if(p[116+0] != IoSpace)
|
|
|
|
break;
|
|
|
|
outb(get32(p+116+4), p[128]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* acpi shutdown failed, try generic reset */
|
|
|
|
archreset();
|
|
|
|
}
|
|
|
|
|
2012-06-17 21:12:19 +00:00
|
|
|
static int identify(void);
|
|
|
|
|
|
|
|
PCArch archacpi = {
|
|
|
|
.id= "ACPI",
|
|
|
|
.ident= identify,
|
2014-12-19 22:34:43 +00:00
|
|
|
.reset= acpireset,
|
2012-06-17 21:12:19 +00:00
|
|
|
.intrinit= acpiinit,
|
|
|
|
.intrenable= mpintrenable,
|
|
|
|
.intron= lapicintron,
|
|
|
|
.introff= lapicintroff,
|
|
|
|
.fastclock= i8253read,
|
|
|
|
.timerset= lapictimerset,
|
|
|
|
};
|
|
|
|
|
|
|
|
static long
|
|
|
|
readtbls(Chan*, void *v, long n, vlong o)
|
|
|
|
{
|
|
|
|
int i, l, m;
|
|
|
|
uchar *p;
|
|
|
|
Tbl *t;
|
|
|
|
|
|
|
|
maptables();
|
|
|
|
|
|
|
|
p = v;
|
2013-06-07 16:03:44 +00:00
|
|
|
for(i=0; n > 0 && i < ntblmap; i++){
|
|
|
|
t = tblmap[i];
|
2012-06-17 21:12:19 +00:00
|
|
|
l = get32(t->len);
|
|
|
|
if(o >= l){
|
|
|
|
o -= l;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
m = l - o;
|
|
|
|
if(m > n)
|
|
|
|
m = n;
|
|
|
|
memmove(p, (uchar*)t + o, m);
|
|
|
|
p += m;
|
|
|
|
n -= m;
|
|
|
|
o = 0;
|
|
|
|
}
|
|
|
|
return p - (uchar*)v;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
identify(void)
|
|
|
|
{
|
2014-10-18 00:01:58 +00:00
|
|
|
uintptr pa;
|
2012-06-17 21:12:19 +00:00
|
|
|
char *cp;
|
|
|
|
|
|
|
|
if((cp = getconf("*acpi")) == nil)
|
|
|
|
return 1;
|
2014-10-18 00:01:58 +00:00
|
|
|
pa = (uintptr)strtoull(cp, nil, 16);
|
|
|
|
if(pa <= 1)
|
|
|
|
rsd = sigsearch("RSD PTR ");
|
2017-03-25 03:08:14 +00:00
|
|
|
else if(pa < MemMin)
|
|
|
|
rsd = KADDR(pa);
|
2014-10-18 00:01:58 +00:00
|
|
|
else
|
|
|
|
rsd = vmap(pa, sizeof(Rsd));
|
|
|
|
if(rsd == nil)
|
2012-08-24 13:36:56 +00:00
|
|
|
return 1;
|
|
|
|
if(checksum(rsd, 20) && checksum(rsd, 36))
|
2012-06-17 21:12:19 +00:00
|
|
|
return 1;
|
|
|
|
addarchfile("acpitbls", 0444, readtbls, nil);
|
2016-10-23 02:09:27 +00:00
|
|
|
addarchfile("acpimem", 0600, readmem, writemem);
|
2012-06-17 21:12:19 +00:00
|
|
|
if(strcmp(cp, "0") == 0)
|
|
|
|
return 1;
|
|
|
|
if((cp = getconf("*nomp")) != nil && strcmp(cp, "0") != 0)
|
|
|
|
return 1;
|
2013-06-25 18:32:43 +00:00
|
|
|
if(m->havetsc && getconf("*notsc") == nil)
|
2013-06-21 00:46:14 +00:00
|
|
|
archacpi.fastclock = tscticks;
|
2012-06-17 21:12:19 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2013-09-06 14:50:43 +00:00
|
|
|
|
|
|
|
static int
|
|
|
|
readpcicfg(Amlio *io, void *data, int n, int offset)
|
|
|
|
{
|
|
|
|
ulong r, x;
|
|
|
|
Pcidev *p;
|
|
|
|
uchar *a;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
a = data;
|
|
|
|
p = io->aux;
|
|
|
|
if(p == nil)
|
|
|
|
return -1;
|
|
|
|
offset += io->off;
|
|
|
|
if(offset > 256)
|
|
|
|
return 0;
|
|
|
|
if(n+offset > 256)
|
|
|
|
n = 256-offset;
|
|
|
|
r = offset;
|
|
|
|
if(!(r & 3) && n == 4){
|
|
|
|
x = pcicfgr32(p, r);
|
|
|
|
PBIT32(a, x);
|
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
if(!(r & 1) && n == 2){
|
|
|
|
x = pcicfgr16(p, r);
|
|
|
|
PBIT16(a, x);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
for(i = 0; i < n; i++){
|
|
|
|
x = pcicfgr8(p, r);
|
|
|
|
PBIT8(a, x);
|
|
|
|
a++;
|
|
|
|
r++;
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2016-10-18 21:04:19 +00:00
|
|
|
static int
|
|
|
|
readec(Amlio *io, void *data, int n, int off)
|
|
|
|
{
|
|
|
|
int port, v;
|
|
|
|
uchar *p;
|
|
|
|
|
|
|
|
USED(io);
|
|
|
|
if(off < 0 || off >= 256)
|
|
|
|
return 0;
|
|
|
|
if(off+n > 256)
|
|
|
|
n = 256 - off;
|
|
|
|
p = data;
|
|
|
|
for(port = off; port < off+n; port++){
|
|
|
|
if((v = ecread(port)) < 0)
|
|
|
|
break;
|
|
|
|
*p++ = v;
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
writeec(Amlio *io, void *data, int n, int off)
|
|
|
|
{
|
|
|
|
int port;
|
|
|
|
uchar *p;
|
|
|
|
|
|
|
|
USED(io);
|
|
|
|
if(off < 0 || off+n > 256)
|
|
|
|
return -1;
|
|
|
|
p = data;
|
|
|
|
for(port = off; port < off+n; port++)
|
|
|
|
if(ecwrite(port, *p++) < 0)
|
|
|
|
break;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2013-09-06 14:50:43 +00:00
|
|
|
static int
|
|
|
|
writepcicfg(Amlio *io, void *data, int n, int offset)
|
|
|
|
{
|
|
|
|
ulong r, x;
|
|
|
|
Pcidev *p;
|
|
|
|
uchar *a;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
a = data;
|
|
|
|
p = io->aux;
|
|
|
|
if(p == nil)
|
|
|
|
return -1;
|
|
|
|
offset += io->off;
|
|
|
|
if(offset > 256)
|
|
|
|
return 0;
|
|
|
|
if(n+offset > 256)
|
|
|
|
n = 256-offset;
|
|
|
|
r = offset;
|
|
|
|
if(!(r & 3) && n == 4){
|
|
|
|
x = GBIT32(a);
|
|
|
|
pcicfgw32(p, r, x);
|
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
if(!(r & 1) && n == 2){
|
|
|
|
x = GBIT16(a);
|
|
|
|
pcicfgw16(p, r, x);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
for(i = 0; i < n; i++){
|
|
|
|
x = GBIT8(a);
|
|
|
|
pcicfgw8(p, r, x);
|
|
|
|
a++;
|
|
|
|
r++;
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
readioport(Amlio *io, void *data, int len, int port)
|
|
|
|
{
|
|
|
|
uchar *a;
|
|
|
|
|
|
|
|
a = data;
|
|
|
|
port += io->off;
|
|
|
|
switch(len){
|
|
|
|
case 4:
|
|
|
|
PBIT32(a, inl(port));
|
|
|
|
return 4;
|
|
|
|
case 2:
|
|
|
|
PBIT16(a, ins(port));
|
|
|
|
return 2;
|
|
|
|
case 1:
|
|
|
|
PBIT8(a, inb(port));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
writeioport(Amlio *io, void *data, int len, int port)
|
|
|
|
{
|
|
|
|
uchar *a;
|
|
|
|
|
|
|
|
a = data;
|
|
|
|
port += io->off;
|
|
|
|
switch(len){
|
|
|
|
case 4:
|
|
|
|
outl(port, GBIT32(a));
|
|
|
|
return 4;
|
|
|
|
case 2:
|
|
|
|
outs(port, GBIT16(a));
|
|
|
|
return 2;
|
|
|
|
case 1:
|
|
|
|
outb(port, GBIT8(a));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
amlmapio(Amlio *io)
|
|
|
|
{
|
|
|
|
int tbdf;
|
|
|
|
Pcidev *pdev;
|
|
|
|
char buf[64];
|
|
|
|
|
|
|
|
switch(io->space){
|
|
|
|
default:
|
|
|
|
print("amlmapio: address space %x not implemented\n", io->space);
|
|
|
|
break;
|
|
|
|
case MemSpace:
|
2016-10-23 02:09:27 +00:00
|
|
|
if(memcheck(io->off, io->len) != io->len){
|
|
|
|
print("amlmapio: [%#p-%#p) overlaps usable memory\n",
|
2016-11-05 17:31:50 +00:00
|
|
|
(uintptr)io->off, (uintptr)(io->off+io->len));
|
2016-10-23 02:09:27 +00:00
|
|
|
break;
|
|
|
|
}
|
2013-09-06 14:50:43 +00:00
|
|
|
if((io->va = vmap(io->off, io->len)) == nil){
|
|
|
|
print("amlmapio: vmap failed\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
case IoSpace:
|
|
|
|
snprint(buf, sizeof(buf), "%N", io->name);
|
|
|
|
if(ioalloc(io->off, io->len, 0, buf) < 0){
|
|
|
|
print("amlmapio: ioalloc failed\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
io->read = readioport;
|
|
|
|
io->write = writeioport;
|
|
|
|
return 0;
|
|
|
|
case PcicfgSpace:
|
|
|
|
if((tbdf = pciaddr(io->name)) < 0){
|
|
|
|
print("amlmapio: no address\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if((pdev = pcimatchtbdf(tbdf)) == nil){
|
|
|
|
print("amlmapio: no device %T\n", tbdf);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
io->aux = pdev;
|
|
|
|
io->read = readpcicfg;
|
|
|
|
io->write = writepcicfg;
|
|
|
|
return 0;
|
2016-10-18 21:04:19 +00:00
|
|
|
case EbctlSpace:
|
|
|
|
io->read = readec;
|
|
|
|
io->write = writeec;
|
|
|
|
return 0;
|
2013-09-06 14:50:43 +00:00
|
|
|
}
|
|
|
|
print("amlmapio: mapping %N failed\n", io->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
amlunmapio(Amlio *io)
|
|
|
|
{
|
|
|
|
switch(io->space){
|
|
|
|
case MemSpace:
|
|
|
|
vunmap(io->va, io->len);
|
|
|
|
break;
|
|
|
|
case IoSpace:
|
|
|
|
iofree(io->off);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void*
|
|
|
|
amlalloc(int n){
|
|
|
|
void *p;
|
|
|
|
|
|
|
|
if((p = malloc(n)) == nil)
|
|
|
|
panic("amlalloc: no memory");
|
|
|
|
memset(p, 0, n);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
amlfree(void *p){
|
|
|
|
free(p);
|
|
|
|
}
|
2013-09-07 12:41:08 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
amldelay(int us)
|
|
|
|
{
|
|
|
|
microdelay(us);
|
|
|
|
}
|