ref: 00f7f2ceb6a1818e2975baf6f5c55c1d2d4ecb6e
dir: /sys/src/9/bcm/bootargs.c/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#define BOOTARGS ((char*)CONFADDR)
#define BOOTARGSLEN ((KZERO+REBOOTADDR)-CONFADDR)
#define MAXCONF 64
static char *confname[MAXCONF];
static char *confval[MAXCONF];
static int nconf;
static char maxmem[256];
static char emmc2bus[38];
static char pciwin[38], pcidmawin[38];
static int
findconf(char *k)
{
int i;
for(i = 0; i < nconf; i++)
if(cistrcmp(confname[i], k) == 0)
return i;
return -1;
}
static void
addconf(char *k, char *v)
{
int i;
i = findconf(k);
if(i < 0){
if(nconf >= MAXCONF)
return;
i = nconf++;
confname[i] = k;
}
confval[i] = v;
}
static void
plan9iniinit(char *s, int cmdline)
{
char *toks[MAXCONF];
int i, c, n;
char *v;
if((c = *s) < ' ' || c >= 0x80)
return;
if(cmdline)
n = tokenize(s, toks, MAXCONF);
else
n = getfields(s, toks, MAXCONF, 1, "\n");
for(i = 0; i < n; i++){
if(toks[i][0] == '#')
continue;
v = strchr(toks[i], '=');
if(v == nil)
continue;
*v++ = '\0';
addconf(toks[i], v);
}
}
typedef struct Devtree Devtree;
struct Devtree
{
uchar *base;
uchar *end;
char *stab;
char path[1024];
};
enum {
DtHeader = 0xd00dfeed,
DtBeginNode = 1,
DtEndNode = 2,
DtProp = 3,
DtEnd = 9,
};
static u32int
beget4(uchar *p)
{
return (u32int)p[0]<<24 | (u32int)p[1]<<16 | (u32int)p[2]<<8 | (u32int)p[3];
}
static void
devtreeprop(char *path, char *key, void *val, int len)
{
uvlong addr, size;
uchar *p = val;
char *s;
if((strcmp(path, "/memory") == 0 || strcmp(path, "/memory@0") == 0)
&& strcmp(key, "reg") == 0){
if(findconf("*maxmem") < 0 && len > 0 && (len % (3*4)) == 0){
addr = (uvlong)beget4(p)<<32 | beget4(p+4);
addr += beget4(p+8);
s = seprint(maxmem, &maxmem[sizeof(maxmem)], "%#llux", addr);
p += 3*4;
len -= 3*4;
while(len > 0){
addr = (uvlong)beget4(p)<<32 | beget4(p+4);
s = seprint(s, &maxmem[sizeof(maxmem)], " %#llux", addr);
addr += beget4(p+8);
s = seprint(s, &maxmem[sizeof(maxmem)], " %#llux", addr);
p += 3*4;
len -= 3*4;
}
addconf("*maxmem", maxmem);
}
return;
}
if(strcmp(path, "/emmc2bus") == 0 && strcmp(key, "dma-ranges") == 0 && len == (2*4 + 2*4 + 1*4)){
if(findconf("*emmc2bus") < 0){
addr = (uvlong)beget4(p+0*4)<<32 | beget4(p+1*4);
addr -= (uvlong)beget4(p+2*4)<<32 | beget4(p+3*4);
size = beget4(p+4*4);
snprint(emmc2bus, sizeof(emmc2bus), "%#llux %#llux", addr, addr+size);
addconf("*emmc2bus", emmc2bus);
}
return;
}
if(strncmp(path, "/scb/pcie@", 10) == 0 && len == (3*4 + 4*4)){
if((beget4(p) & 0x3000000) == 0x2000000){
size = (uvlong)beget4(p+5*4)<<32 | beget4(p+5*4+4);
if(strcmp(key, "ranges") == 0 && findconf("*pciwin") < 0){
addr = (uvlong)beget4(p+3*4)<<32 | beget4(p+4*4);
snprint(pciwin, sizeof(pciwin), "%#llux %#llux", addr, addr+size);
addconf("*pciwin", pciwin);
} else if(strcmp(key, "dma-ranges") == 0 && findconf("*pcidmawin") < 0){
addr = (uvlong)beget4(p+1*4)<<32 | beget4(p+2*4);
addr -= (uvlong)beget4(p+3*4)<<32 | beget4(p+4*4);
snprint(pcidmawin, sizeof(pcidmawin), "%#llux %#llux", addr, addr+size);
addconf("*pcidmawin", pcidmawin);
}
}
return;
}
if(strcmp(path, "/chosen") == 0 && strcmp(key, "bootargs") == 0){
if(len > BOOTARGSLEN)
len = BOOTARGSLEN;
memmove(BOOTARGS, val, len);
plan9iniinit(BOOTARGS, 1);
return;
}
}
static uchar*
devtreenode(Devtree *t, uchar *p, char *cp)
{
uchar *e = (uchar*)t->stab;
char *s;
int n;
if(p+4 > e || beget4(p) != DtBeginNode)
return nil;
p += 4;
if((s = memchr((char*)p, 0, e - p)) == nil)
return nil;
n = s - (char*)p;
cp += n;
if(cp >= &t->path[sizeof(t->path)])
return nil;
memmove(cp - n, (char*)p, n);
*cp = 0;
p += (n + 4) & ~3;
while(p+12 <= e && beget4(p) == DtProp){
n = beget4(p+4);
if(p + 12 + n > e)
return nil;
s = t->stab + beget4(p+8);
if(s < t->stab || s >= (char*)t->end
|| memchr(s, 0, (char*)t->end - s) == nil)
return nil;
devtreeprop(t->path, s, p+12, n);
p += 12 + ((n + 3) & ~3);
}
while(p+4 <= e && beget4(p) == DtBeginNode){
*cp = '/';
p = devtreenode(t, p, cp+1);
if(p == nil)
return nil;
}
if(p+4 > e || beget4(p) != DtEndNode)
return nil;
return p+4;
}
static int
parsedevtree(uchar *base, uintptr len)
{
Devtree t[1];
u32int total;
if(len < 28 || beget4(base) != DtHeader)
return -1;
total = beget4(base+4);
if(total < 28 || total > len)
return -1;
t->base = base;
t->end = t->base + total;
t->stab = (char*)base + beget4(base+12);
if(t->stab >= (char*)t->end)
return -1;
devtreenode(t, base + beget4(base+8), t->path);
return 0;
}
typedef struct Atag Atag;
struct Atag {
u32int size; /* size of atag in words, including this header */
u32int tag; /* atag type */
union {
u32int data[1]; /* actually [size-2] */
/* AtagMem */
struct {
u32int size;
u32int base;
} mem;
/* AtagCmdLine */
char cmdline[1]; /* actually [4*(size-2)] */
};
};
enum {
AtagNone = 0x00000000,
AtagCore = 0x54410001,
AtagMem = 0x54410002,
AtagCmdline = 0x54410009,
};
static int
parseatags(char *base, uintptr len)
{
char x, *e = base;
Atag *a;
if(e+8 > base+len)
return -1;
a = (Atag*)e;
if(a->tag != AtagCore)
return -1;
while(a->tag != AtagNone){
e += a->size * sizeof(u32int);
if(a->size < 2 || e < (char*)a || e > base+len)
break;
switch(a->tag){
case AtagMem:
if(findconf("*maxmem") < 0){
snprint(maxmem, sizeof(maxmem), "%ud", a->mem.base+a->mem.size);
addconf("*maxmem", maxmem);
}
break;
case AtagCmdline:
x = *e;
*e = 0;
plan9iniinit(a->cmdline, 1);
*e = x;
break;
}
if(e+8 > base+len)
break;
a = (Atag*)e;
}
return 0;
}
void
bootargsinit(uintptr pa)
{
uintptr len;
/*
* kernel gets DTB/ATAGS pointer on entry
*/
if(pa != 0 && (len = cankaddr(pa)) != 0){
void *va = KADDR(pa);
if(parseatags(va, len) == 0 || parsedevtree(va, len) == 0)
return;
}
/*
* /dev/reboot case, check CONFADDR
*/
if(parseatags(BOOTARGS, BOOTARGSLEN) != 0)
plan9iniinit(BOOTARGS, 0);
}
char*
getconf(char *name)
{
int i;
if((i = findconf(name)) < 0)
return nil;
return confval[i];
}
void
setconfenv(void)
{
int i;
for(i = 0; i < nconf; i++){
if(confname[i][0] != '*')
ksetenv(confname[i], confval[i], 0);
ksetenv(confname[i], confval[i], 1);
}
}
void
writeconf(void)
{
char *p, *q;
int n;
p = getconfenv();
if(waserror()) {
free(p);
nexterror();
}
/* convert to name=value\n format */
for(q=p; *q; q++) {
q += strlen(q);
*q = '=';
q += strlen(q);
*q = '\n';
}
n = q - p + 1;
if(n >= BOOTARGSLEN)
error("kernel configuration too large");
memmove(BOOTARGS, p, n);
memset(BOOTARGS+n, 0, BOOTARGSLEN-n);
poperror();
free(p);
}