#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ofw_machdep.c,v 1.36 2022/12/12 13:26:46 martin Exp $");
#include <sys/param.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/disk.h>
#include <sys/disklabel.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/systm.h>
#include <dev/cons.h>
#include <dev/ofw/openfirm.h>
#include <machine/powerpc.h>
#include <machine/autoconf.h>
#include <powerpc/ofw_machdep.h>
#ifdef DEBUG
#define DPRINTF ofprint
#else
#define DPRINTF while(0) printf
#endif
#define ofpanic(FORMAT, ...) do { \
ofprint(FORMAT __VA_OPT__(,) __VA_ARGS__); \
panic(FORMAT __VA_OPT__(,) __VA_ARGS__); \
} while (0)
int ofw_root;
int ofw_chosen;
bool ofw_real_mode;
int console_node = -1, console_instance = -1;
int ofw_stdin, ofw_stdout;
bool ofwbootcons_suppress;
int ofw_address_cells;
int ofw_size_cells;
void ofprint(const char *blah, ...)
{
va_list va;
char buf[256];
int len;
va_start(va, blah);
len = vsnprintf(buf, sizeof(buf), blah, va);
va_end(va);
OF_write(console_instance, buf, len);
if ((len > 0) && (buf[len - 1] == '\n'))
OF_write(console_instance, "\r", 1);
}
static int
ofwbootcons_cngetc(dev_t dev)
{
unsigned char ch = '\0';
int l;
if (ofwbootcons_suppress) {
return ch;
}
while ((l = OF_read(ofw_stdin, &ch, 1)) != 1) {
if (l != -2 && l != 0) {
return -1;
}
}
return ch;
}
static void
ofwbootcons_cnputc(dev_t dev, int c)
{
char ch = c;
if (ofwbootcons_suppress) {
return;
}
OF_write(ofw_stdout, &ch, 1);
}
static struct consdev consdev_ofwbootcons = {
.cn_getc = ofwbootcons_cngetc,
.cn_putc = ofwbootcons_cnputc,
.cn_pollc = nullcnpollc,
.cn_dev = NODEV,
.cn_pri = CN_INTERNAL,
};
static void
ofw_bootstrap_console(void)
{
int node;
if (ofw_chosen == -1) {
goto nocons;
}
if (OF_getprop(ofw_chosen, "stdout", &ofw_stdout,
sizeof(ofw_stdout)) != sizeof(ofw_stdout))
goto nocons;
if (OF_getprop(ofw_chosen, "stdin", &ofw_stdin,
sizeof(ofw_stdin)) != sizeof(ofw_stdin))
goto nocons;
if (ofw_stdout == 0) {
ofw_stdout = OF_open("screen");
}
node = OF_instance_to_package(ofw_stdout);
console_node = node;
console_instance = ofw_stdout;
cn_tab = &consdev_ofwbootcons;
return;
nocons:
ofpanic("No /chosen could be found!\n");
console_node = -1;
}
#define OFMEM_REGIONS 32
static struct mem_region OFmem[OFMEM_REGIONS + 1], OFavail[OFMEM_REGIONS + 3];
static void
ofw_bootstrap_get_memory(void)
{
const char *macrisc[] = {"MacRISC", "MacRISC2", "MacRISC4", NULL};
int hmem, i, cnt, memcnt, regcnt;
int numregs;
uint32_t regs[OFMEM_REGIONS * 4];
int acells = ofw_address_cells;
int scells = ofw_size_cells;
DPRINTF("calling mem_regions\n");
memset(regs, 0, sizeof(regs));
if ((hmem = OF_finddevice("/memory")) == -1)
goto error;
regcnt = OF_getprop(hmem, "reg", regs,
sizeof(regs[0]) * OFMEM_REGIONS * 4);
if (regcnt <= 0)
goto error;
numregs = regcnt / (sizeof(uint32_t) * (acells + scells));
DPRINTF("regcnt=%d num=%d acell=%d scell=%d\n",
regcnt, numregs, acells, scells);
memset(OFmem, 0, sizeof(OFmem));
for (i = 0, memcnt = 0; i < numregs; i++) {
uint64_t addr, size;
if (acells > 1)
memcpy(&addr, ®s[i * (acells + scells)],
sizeof(int32_t) * acells);
else
addr = regs[i * (acells + scells)];
if (scells > 1)
memcpy(&size, ®s[i * (acells + scells) + acells],
sizeof(int32_t) * scells);
else
size = regs[i * (acells + scells) + acells];
if (size == 0)
continue;
#ifndef _LP64
if (addr > 0xFFFFFFFF || size > 0xFFFFFFFF ||
(addr + size) > 0xFFFFFFFF) {
ofprint("Base addr of %llx or size of %llx too"
" large for 32 bit OS. Skipping.", addr, size);
continue;
}
#endif
OFmem[memcnt].start = addr;
OFmem[memcnt].size = size;
DPRINTF("mem region %d start=%"PRIx64" size=%"PRIx64"\n",
memcnt, addr, size);
memcnt++;
}
DPRINTF("available\n");
memset(regs, 0, sizeof(regs));
regcnt = OF_getprop(hmem, "available", regs,
sizeof(regs[0]) * OFMEM_REGIONS * 4);
if (regcnt <= 0)
goto error;
DPRINTF("%08x %08x %08x %08x\n", regs[0], regs[1], regs[2], regs[3]);
if (of_compatible(ofw_root, macrisc)) {
DPRINTF("this appears to be a mac...\n");
acells = 1;
scells = 1;
}
numregs = regcnt / (sizeof(uint32_t) * (acells + scells));
DPRINTF("regcnt=%d num=%d acell=%d scell=%d\n",
regcnt, numregs, acells, scells);
DPRINTF("to OF_avail\n");
memset(OFavail, 0, sizeof(OFavail));
for (i = 0, cnt = 0; i < numregs; i++) {
uint64_t addr, size;
DPRINTF("%d\n", i);
if (acells > 1)
memcpy(&addr, ®s[i * (acells + scells)],
sizeof(int32_t) * acells);
else
addr = regs[i * (acells + scells)];
if (scells > 1)
memcpy(&size, ®s[i * (acells + scells) + acells],
sizeof(int32_t) * scells);
else
size = regs[i * (acells + scells) + acells];
if (size == 0)
continue;
#ifndef _LP64
if (addr > 0xFFFFFFFF || size > 0xFFFFFFFF ||
(addr + size) > 0xFFFFFFFF) {
ofprint("Base addr of %llx or size of %llx too"
" large for 32 bit OS. Skipping.", addr, size);
continue;
}
#endif
OFavail[cnt].start = addr;
OFavail[cnt].size = size;
DPRINTF("avail region %d start=%#"PRIx64" size=%#"PRIx64"\n",
cnt, addr, size);
cnt++;
}
if (strncmp(model_name, "Pegasos", 7) == 0) {
#define AVAIL_THRESH (0x10000000-1)
if (((OFavail[cnt-1].start + OFavail[cnt-1].size +
AVAIL_THRESH) & ~AVAIL_THRESH) <
(OFmem[memcnt-1].start + OFmem[memcnt-1].size)) {
OFavail[cnt].start =
(OFavail[cnt-1].start + OFavail[cnt-1].size +
AVAIL_THRESH) & ~AVAIL_THRESH;
OFavail[cnt].size =
OFmem[memcnt-1].size - OFavail[cnt].start;
ofprint("WARNING: add memory segment %lx - %" PRIxPADDR ","
"\nWARNING: which was not recognized by "
"the Firmware.\n",
(unsigned long)OFavail[cnt].start,
(unsigned long)OFavail[cnt].start +
OFavail[cnt].size);
cnt++;
}
}
return;
error:
#if defined (MAMBO)
ofprint("no memory, assuming 512MB\n");
OFmem[0].start = 0x0;
OFmem[0].size = 0x20000000;
OFavail[0].start = 0x3000;
OFavail[0].size = 0x20000000 - 0x3000;
#else
ofpanic("no memory?");
#endif
return;
}
static void
ofw_bootstrap_get_translations(void)
{
uint32_t regs[OFW_MAX_TRANSLATIONS * 5];
uint32_t virt, size, mode;
uint64_t phys;
uint32_t *rp;
int proplen;
int mmu_ihandle, mmu_phandle;
int idx;
if (OF_getprop(ofw_chosen, "mmu", &mmu_ihandle,
sizeof(mmu_ihandle)) <= 0) {
ofprint("No /chosen/mmu\n");
return;
}
mmu_phandle = OF_instance_to_package(mmu_ihandle);
proplen = OF_getproplen(mmu_phandle, "translations");
if (proplen <= 0) {
ofprint("No translations in /chosen/mmu\n");
return;
}
if (proplen > sizeof(regs)) {
ofpanic("/chosen/mmu translations too large");
}
proplen = OF_getprop(mmu_phandle, "translations", regs, sizeof(regs));
int nregs = proplen / sizeof(regs[0]);
for (idx = 0, rp = regs; rp < ®s[nregs];) {
virt = *rp++;
size = *rp++;
switch (ofw_address_cells) {
case 1:
phys = *rp++;
break;
case 2:
phys = *rp++;
phys = (phys << 32) | *rp++;
break;
default:
ofpanic("unexpected #address-cells");
}
mode = *rp++;
if (rp > ®s[nregs]) {
ofpanic("unexpected OFW translations format");
}
if (size == 0) {
continue;
}
DPRINTF("translation %d virt=%#"PRIx32
" phys=%#"PRIx64" size=%#"PRIx32" mode=%#"PRIx32"\n",
idx, virt, phys, size, mode);
if (sizeof(paddr_t) < 8 && phys >= 0x100000000ULL) {
ofpanic("translation phys out of range");
}
if (idx == OFW_MAX_TRANSLATIONS) {
ofpanic("too many OFW translations");
}
ofw_translations[idx].virt = virt;
ofw_translations[idx].size = size;
ofw_translations[idx].phys = (paddr_t)phys;
ofw_translations[idx].mode = mode;
idx++;
}
}
static bool
ofw_option_truefalse(const char *prop, int proplen)
{
switch (prop[0]) {
case 'y':
case 'Y':
case 't':
case 'T':
case '1':
return true;
}
return false;
}
void
ofw_bootstrap(void)
{
char prop[32];
int handle, proplen;
ofw_root = OF_finddevice("/");
ofw_chosen = OF_finddevice("/chosen");
ofw_bootstrap_console();
handle = OF_finddevice("/options");
if (handle != -1) {
proplen = OF_getprop(handle, "real-mode?", prop, sizeof(prop));
if (proplen > 0) {
ofw_real_mode = ofw_option_truefalse(prop, proplen);
} else {
ofw_real_mode = false;
}
}
DPRINTF("OpenFirmware running in %s-mode\n",
ofw_real_mode ? "real" : "virtual");
if (OF_getprop(ofw_root, "#address-cells", &ofw_address_cells,
sizeof(ofw_address_cells)) <= 0)
ofw_address_cells = 1;
if (OF_getprop(ofw_root, "#size-cells", &ofw_size_cells,
sizeof(ofw_size_cells)) <= 0)
ofw_size_cells = 1;
ofw_bootstrap_get_memory();
ofw_bootstrap_get_translations();
}
void
mem_regions(struct mem_region **memp, struct mem_region **availp)
{
*memp = OFmem;
*availp = OFavail;
}
void
ppc_exit(void)
{
OF_exit();
}
void
ppc_boot(char *str)
{
OF_boot(str);
}