#include "opt_multiprocessor.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ofw_machdep.c,v 1.51 2022/05/14 07:11:23 hgutch 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/kprintf.h>
#include <sys/malloc.h>
#include <sys/stat.h>
#include <sys/systm.h>
#include <machine/openfirm.h>
#include <machine/promlib.h>
#include <dev/ofw/ofw_pci.h>
#include <machine/sparc64.h>
static u_int mmuh = -1, memh = -1;
static u_int get_mmu_handle(void);
static u_int get_memory_handle(void);
static u_int
get_mmu_handle(void)
{
u_int chosen;
if ((chosen = OF_finddevice("/chosen")) == -1) {
prom_printf("get_mmu_handle: cannot get /chosen\n");
return -1;
}
if (OF_getprop(chosen, "mmu", &mmuh, sizeof(mmuh)) == -1) {
prom_printf("get_mmu_handle: cannot get mmuh\n");
return -1;
}
return mmuh;
}
static u_int
get_memory_handle(void)
{
u_int chosen;
if ((chosen = OF_finddevice("/chosen")) == -1) {
prom_printf("get_memory_handle: cannot get /chosen\n");
return -1;
}
if (OF_getprop(chosen, "memory", &memh, sizeof(memh)) == -1) {
prom_printf("get_memory_handle: cannot get memh\n");
return -1;
}
return memh;
}
int
prom_set_trap_table_sun4u(vaddr_t tba)
{
struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
cell_t tba;
} args;
args.name = ADR2CELL(&"SUNW,set-trap-table");
args.nargs = 1;
args.nreturns = 0;
args.tba = ADR2CELL(tba);
return openfirmware(&args);
}
#ifdef SUN4V
int
prom_set_trap_table_sun4v(vaddr_t tba, paddr_t mmfsa)
{
struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
cell_t tba;
cell_t mmfsa;
} args;
args.name = ADR2CELL("SUNW,set-trap-table");
args.nargs = 2;
args.nreturns = 0;
args.tba = ADR2CELL(tba);
args.mmfsa = ADR2CELL(mmfsa);
return openfirmware(&args);
}
#endif
paddr_t
prom_vtop(vaddr_t vaddr)
{
struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
cell_t method;
cell_t ihandle;
cell_t vaddr;
cell_t status;
cell_t retaddr;
cell_t mode;
cell_t phys_hi;
cell_t phys_lo;
} args;
if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
prom_printf("prom_vtop: cannot get mmuh\n");
return 0;
}
args.name = ADR2CELL(&"call-method");
args.nargs = 3;
args.nreturns = 5;
args.method = ADR2CELL(&"translate");
args.ihandle = HDL2CELL(mmuh);
args.vaddr = ADR2CELL(vaddr);
if (openfirmware(&args) == -1)
return -1;
#if 0
prom_printf("Called \"translate\", mmuh=%x, vaddr=%x, status=%x %x,\n retaddr=%x %x, mode=%x %x, phys_hi=%x %x, phys_lo=%x %x\n",
mmuh, vaddr, (int)(args.status>>32), (int)args.status, (int)(args.retaddr>>32), (int)args.retaddr,
(int)(args.mode>>32), (int)args.mode, (int)(args.phys_hi>>32), (int)args.phys_hi,
(int)(args.phys_lo>>32), (int)args.phys_lo);
#endif
return (paddr_t)CELL2HDQ(args.phys_hi, args.phys_lo);
}
vaddr_t
prom_claim_virt(vaddr_t vaddr, int len)
{
struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
cell_t method;
cell_t ihandle;
cell_t align;
cell_t len;
cell_t vaddr;
cell_t status;
cell_t retaddr;
} args;
if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
prom_printf("prom_claim_virt: cannot get mmuh\n");
return 0;
}
args.name = ADR2CELL(&"call-method");
args.nargs = 5;
args.nreturns = 2;
args.method = ADR2CELL(&"claim");
args.ihandle = HDL2CELL(mmuh);
args.align = 0;
args.len = len;
args.vaddr = ADR2CELL(vaddr);
if (openfirmware(&args) == -1)
return -1;
return (vaddr_t)args.retaddr;
}
vaddr_t
prom_alloc_virt(int len, int align)
{
struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
cell_t method;
cell_t ihandle;
cell_t align;
cell_t len;
cell_t status;
cell_t retaddr;
} args;
if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
prom_printf("prom_alloc_virt: cannot get mmuh\n");
return -1LL;
}
args.name = ADR2CELL(&"call-method");
args.nargs = 4;
args.nreturns = 2;
args.method = ADR2CELL(&"claim");
args.ihandle = HDL2CELL(mmuh);
args.align = align;
args.len = len;
if (openfirmware(&args) != 0)
return -1;
return (vaddr_t)args.retaddr;
}
int
prom_free_virt(vaddr_t vaddr, int len)
{
struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
cell_t method;
cell_t ihandle;
cell_t len;
cell_t vaddr;
} args;
if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
prom_printf("prom_free_virt: cannot get mmuh\n");
return -1;
}
args.name = ADR2CELL(&"call-method");
args.nargs = 4;
args.nreturns = 0;
args.method = ADR2CELL(&"release");
args.ihandle = HDL2CELL(mmuh);
args.vaddr = ADR2CELL(vaddr);
args.len = len;
return openfirmware(&args);
}
int
prom_unmap_virt(vaddr_t vaddr, int len)
{
struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
cell_t method;
cell_t ihandle;
cell_t len;
cell_t vaddr;
} args;
if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
prom_printf("prom_unmap_virt: cannot get mmuh\n");
return -1;
}
args.name = ADR2CELL(&"call-method");
args.nargs = 4;
args.nreturns = 0;
args.method = ADR2CELL(&"unmap");
args.ihandle = HDL2CELL(mmuh);
args.vaddr = ADR2CELL(vaddr);
args.len = len;
return openfirmware(&args);
}
int
prom_map_phys(paddr_t paddr, off_t size, vaddr_t vaddr, int mode)
{
struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
cell_t method;
cell_t ihandle;
cell_t mode;
cell_t size;
cell_t vaddr;
cell_t phys_hi;
cell_t phys_lo;
} args;
if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
prom_printf("prom_map_phys: cannot get mmuh\n");
return 0;
}
args.name = ADR2CELL(&"call-method");
args.nargs = 7;
args.nreturns = 0;
args.method = ADR2CELL(&"map");
args.ihandle = HDL2CELL(mmuh);
args.mode = mode;
args.size = size;
args.vaddr = ADR2CELL(vaddr);
args.phys_hi = HDQ2CELL_HI(paddr);
args.phys_lo = HDQ2CELL_LO(paddr);
if (openfirmware(&args) == -1)
return -1;
return 0;
}
paddr_t
prom_alloc_phys(int len, int align)
{
struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
cell_t method;
cell_t ihandle;
cell_t align;
cell_t len;
cell_t status;
cell_t phys_hi;
cell_t phys_lo;
} args;
if (memh == -1 && ((memh = get_memory_handle()) == -1)) {
prom_printf("prom_alloc_phys: cannot get memh\n");
return -1;
}
args.name = ADR2CELL(&"call-method");
args.nargs = 4;
args.nreturns = 3;
args.method = ADR2CELL(&"claim");
args.ihandle = HDL2CELL(memh);
args.align = align;
args.len = len;
if (openfirmware(&args) != 0)
return -1;
return (paddr_t)CELL2HDQ(args.phys_hi, args.phys_lo);
}
paddr_t
prom_claim_phys(paddr_t phys, int len)
{
struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
cell_t method;
cell_t ihandle;
cell_t align;
cell_t len;
cell_t phys_hi;
cell_t phys_lo;
cell_t status;
cell_t rphys_hi;
cell_t rphys_lo;
} args;
if (memh == -1 && ((memh = get_memory_handle()) == -1)) {
prom_printf("prom_claim_phys: cannot get memh\n");
return -1;
}
args.name = ADR2CELL(&"call-method");
args.nargs = 6;
args.nreturns = 3;
args.method = ADR2CELL(&"claim");
args.ihandle = HDL2CELL(memh);
args.align = 0;
args.len = len;
args.phys_hi = HDQ2CELL_HI(phys);
args.phys_lo = HDQ2CELL_LO(phys);
if (openfirmware(&args) != 0)
return -1;
return (paddr_t)CELL2HDQ(args.rphys_hi, args.rphys_lo);
}
int
prom_free_phys(paddr_t phys, int len)
{
struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
cell_t method;
cell_t ihandle;
cell_t len;
cell_t phys_hi;
cell_t phys_lo;
} args;
if (memh == -1 && ((memh = get_memory_handle()) == -1)) {
prom_printf("prom_free_phys: cannot get memh\n");
return -1;
}
args.name = ADR2CELL(&"call-method");
args.nargs = 5;
args.nreturns = 0;
args.method = ADR2CELL(&"release");
args.ihandle = HDL2CELL(memh);
args.len = len;
args.phys_hi = HDQ2CELL_HI(phys);
args.phys_lo = HDQ2CELL_LO(phys);
return openfirmware(&args);
}
paddr_t
prom_get_msgbuf(int len, int align)
{
struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
cell_t method;
cell_t ihandle;
cell_t align;
cell_t len;
cell_t id;
cell_t status;
cell_t phys_hi;
cell_t phys_lo;
} args;
paddr_t addr;
if (memh == -1 && ((memh = get_memory_handle()) == -1)) {
prom_printf("prom_get_msgbuf: cannot get memh\n");
return -1;
}
if (OF_test("test-method") == 0) {
if (OF_test_method(OF_instance_to_package(memh),
"SUNW,retain") == 0) {
args.name = ADR2CELL(&"call-method");
args.nargs = 5;
args.nreturns = 3;
args.method = ADR2CELL(&"SUNW,retain");
args.id = ADR2CELL(&"msgbuf");
args.ihandle = HDL2CELL(memh);
args.len = len;
args.align = align;
args.status = -1;
if (openfirmware(&args) == 0 && args.status == 0) {
return (paddr_t)CELL2HDQ(args.phys_hi, args.phys_lo);
} else prom_printf("prom_get_msgbuf: SUNW,retain failed\n");
} else prom_printf("prom_get_msgbuf: test-method failed\n");
} else prom_printf("prom_get_msgbuf: test failed\n");
addr = prom_claim_phys(0x000, len);
prom_printf("prom_get_msgbuf: allocated new buf at %08x\n", (int)addr);
if (addr == -1) {
prom_printf("prom_get_msgbuf: cannot get allocate physmem\n");
return -1;
}
prom_printf("prom_get_msgbuf: claiming new buf at %08x\n", (int)addr);
{ int i; for (i=0; i<200000000; i++); }
return addr;
}
#ifdef MULTIPROCESSOR
void
prom_startcpu(u_int cpu, void *func, u_long arg)
{
static struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
cell_t cpu;
cell_t func;
cell_t arg;
} args;
args.name = ADR2CELL(&"SUNW,start-cpu");
args.nargs = 3;
args.nreturns = 0;
args.cpu = cpu;
args.func = (cell_t)(u_long)func;
args.arg = (cell_t)arg;
openfirmware(&args);
}
int
prom_startcpu_by_cpuid(u_int cpu, void *func, u_long arg)
{
static struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
cell_t cpu;
cell_t func;
cell_t arg;
cell_t status;
} args;
if (OF_test("SUNW,start-cpu-by-cpuid") != 0)
return -1;
args.name = ADR2CELL("SUNW,start-cpu-by-cpuid");
args.nargs = 3;
args.nreturns = 1;
args.cpu = cpu;
args.func = ADR2CELL(func);
args.arg = arg;
return openfirmware(&args);
}
void
prom_stopself(void)
{
extern void openfirmware_exit(void*);
static struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
} args;
args.name = ADR2CELL(&"SUNW,stop-self");
args.nargs = 0;
args.nreturns = 0;
openfirmware_exit(&args);
panic("prom_stopself: failed.");
}
bool
prom_has_stopself(void)
{
return OF_test("SUNW,stop-self") == 0;
}
int
prom_stop_other(u_int id)
{
static struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
cell_t cpuid;
cell_t result;
} args;
args.name = ADR2CELL(&"SUNW,stop-cpu-by-cpuid");
args.nargs = 1;
args.nreturns = 1;
args.cpuid = id;
args.result = 0;
if (openfirmware(&args) == -1)
return -1;
return args.result;
}
bool
prom_has_stop_other(void)
{
return OF_test("SUNW,stop-cpu-by-cpuid") == 0;
}
#endif
uint64_t
prom_set_sun4v_api_version(uint64_t api_group, uint64_t major,
uint64_t minor, uint64_t *supported_minor)
{
static struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
cell_t api_group;
cell_t major;
cell_t minor;
cell_t status;
cell_t supported_minor;
} args;
args.name = ADR2CELL("SUNW,set-sun4v-api-version");
args.nargs = 3;
args.nreturns = 2;
args.api_group = api_group;
args.major = major;
args.minor = minor;
args.status = -1;
args.supported_minor = -1;
openfirmware(&args);
*supported_minor = args.supported_minor;
return (uint64_t)args.status;
}
#if 1
uint64_t
prom_get_sun4v_api_version(uint64_t api_group, uint64_t* major, uint64_t* minor)
{
static struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
cell_t api_group;
cell_t status;
cell_t major;
cell_t minor;
} args;
args.name = ADR2CELL("SUNW,get-sun4v-api-version");
args.nargs = 1;
args.nreturns = 3;
args.api_group = api_group;
args.status = -1;
args.major = -1;
args.minor = -1;
openfirmware(&args);
*major = args.major;
*minor = args.minor;
return (uint64_t)args.status;
}
#endif
void
prom_sun4v_soft_state_supported(void)
{
static struct {
cell_t name;
cell_t nargs;
cell_t nreturns;
} args;
args.name = ADR2CELL("SUNW,soft-state-supported");
args.nargs = 0;
args.nreturns = 0;
openfirmware(&args);
}
#ifdef DEBUG
int ofmapintrdebug = 0;
#define DPRINTF(x) if (ofmapintrdebug) printf x
#else
#define DPRINTF(x)
#endif
int
OF_searchprop(int node, const char *prop, void *sbuf, int buflen)
{
int len;
for( ; node; node = OF_parent(node)) {
len = OF_getprop(node, prop, sbuf, buflen);
if (len >= 0)
return (len);
}
return (-1);
}
static int compare_cells (int *cell1, int *cell2, int *mask, int ncells);
static int
compare_cells(int *cell1, int *cell2, int *mask, int ncells)
{
int i;
for (i=0; i<ncells; i++) {
DPRINTF(("src %x ^ dest %x -> %x & mask %x -> %x\n",
cell1[i], cell2[i], (cell1[i] ^ cell2[i]),
mask[i], ((cell1[i] ^ cell2[i]) & mask[i])));
if (((cell1[i] ^ cell2[i]) & mask[i]) != 0)
return (0);
}
return (1);
}
static int
find_pci_host_node(int node)
{
char dev_type[16];
int pch = 0;
int len;
for (; node; node = OF_parent(node)) {
len = OF_getprop(node, "device_type",
&dev_type, sizeof(dev_type));
if (len <= 0)
continue;
if (!strcmp(dev_type, "pci") ||
!strcmp(dev_type, "pciex"))
pch = node;
}
return pch;
}
int
OF_mapintr(int node, int *interrupt, int validlen, int buflen)
{
int i, len;
int address_cells, size_cells, interrupt_cells, interrupt_map_len;
int static_interrupt_map[256];
int interrupt_map_mask[10];
int *interrupt_map = &static_interrupt_map[0];
int maplen = sizeof static_interrupt_map;
int *free_map = NULL;
int reg[10];
char dev_type[32];
int phc_node;
int rc = -1;
phc_node = find_pci_host_node(node);
if (*interrupt & 0x20 || *interrupt & 0x7c0) {
char model[40];
if (OF_getprop(phc_node, "model", &model, sizeof(model)) > 10
&& !strcmp(model, "SUNW,psycho")) {
DPRINTF(("OF_mapintr: interrupt %x already mapped\n",
*interrupt));
return validlen;
}
}
if ((len = OF_getprop(node, "reg", ®, sizeof(reg))) <= 0) {
printf("OF_mapintr: no reg property?\n");
return (-1);
}
while (node) {
#ifdef DEBUG
char name[40];
if (ofmapintrdebug) {
OF_getprop(node, "name", &name, sizeof(name));
printf("Node %s (%x), host %x\n", name,
node, phc_node);
}
#endif
retry_map:
if ((interrupt_map_len = OF_getprop(node,
"interrupt-map", interrupt_map, maplen)) <= 0) {
if (((len = OF_getprop(node, "device_type", &dev_type,
sizeof(dev_type))) > 0) &&
(!strcmp(dev_type, "pci") ||
!strcmp(dev_type, "pciex")) &&
(node != phc_node)) {
#ifdef DEBUG
int ointerrupt = *interrupt;
#endif
*interrupt = ((*interrupt +
OFW_PCI_PHYS_HI_DEVICE(reg[0]) - 1) & 3) + 1;
DPRINTF(("OF_mapintr: interrupt %x -> %x, reg[0] %x\n",
ointerrupt, *interrupt, reg[0]));
}
reg[0] = 0;
OF_getprop(node, "reg", ®, sizeof(reg));
node = OF_parent(node);
continue;
}
if (interrupt_map_len > maplen) {
DPRINTF(("interrupt_map_len %d > maplen %d, "
"allocating\n", interrupt_map_len, maplen));
KASSERT(!free_map);
free_map = malloc(interrupt_map_len, M_DEVBUF,
M_NOWAIT);
if (!free_map) {
interrupt_map_len = sizeof static_interrupt_map;
} else {
interrupt_map = free_map;
maplen = interrupt_map_len;
goto retry_map;
}
}
interrupt_map_len = interrupt_map_len/sizeof(int);
if ((len = (OF_searchprop(node, "#address-cells",
&address_cells, sizeof(address_cells)))) <= 0) {
address_cells = 2;
}
DPRINTF(("#address-cells = %d len %d ", address_cells, len));
if ((len = OF_searchprop(node, "#size-cells", &size_cells,
sizeof(size_cells))) <= 0) {
size_cells = 2;
}
DPRINTF(("#size-cells = %d len %d ", size_cells, len));
if ((len = OF_getprop(node, "#interrupt-cells", &interrupt_cells,
sizeof(interrupt_cells))) <= 0) {
interrupt_cells = 1;
}
DPRINTF(("#interrupt-cells = %d, len %d\n", interrupt_cells,
len));
if ((len = OF_getprop(node, "interrupt-map-mask", &interrupt_map_mask,
sizeof(interrupt_map_mask))) <= 0) {
for (i = 0; i<(address_cells + interrupt_cells); i++)
interrupt_map_mask[i] = -1;
}
#ifdef DEBUG
DPRINTF(("interrupt-map-mask len %d = ", len));
for (i=0; i<(address_cells + interrupt_cells); i++)
DPRINTF(("%x.", interrupt_map_mask[i]));
DPRINTF(("reg = "));
for (i=0; i<(address_cells); i++)
DPRINTF(("%x.", reg[i]));
DPRINTF(("interrupts = "));
for (i=0; i<(interrupt_cells); i++)
DPRINTF(("%x.", interrupt[i]));
#endif
i = 0;
while (i < interrupt_map_len + address_cells + interrupt_cells) {
int pintr_cells;
int *imap = &interrupt_map[i];
int *parent = &imap[address_cells + interrupt_cells];
#ifdef DEBUG
DPRINTF(("\ninterrupt-map addr "));
for (len = 0; len < address_cells; len++)
DPRINTF(("%x.", imap[len]));
DPRINTF((" intr "));
for (; len < (address_cells+interrupt_cells); len++)
DPRINTF(("%x.", imap[len]));
DPRINTF(("\nnode %x vs parent %x\n",
imap[len], *parent));
#endif
if ((len = OF_searchprop(*parent, "#interrupt-cells",
&pintr_cells, sizeof(pintr_cells))) < 0) {
pintr_cells = interrupt_cells;
}
DPRINTF(("pintr_cells = %d len %d\n", pintr_cells, len));
if (compare_cells(imap, reg,
interrupt_map_mask, address_cells) &&
compare_cells(&imap[address_cells],
interrupt,
&interrupt_map_mask[address_cells],
interrupt_cells))
{
if (buflen < pintr_cells) {
if (free_map)
free(free_map, M_DEVBUF);
return (-1);
}
node = *parent;
parent++;
#ifdef DEBUG
DPRINTF(("Match! using "));
for (len = 0; len < pintr_cells; len++)
DPRINTF(("%x.", parent[len]));
DPRINTF(("\n"));
#endif
for (i = 0; i < pintr_cells; i++)
interrupt[i] = parent[i];
rc = validlen = pintr_cells;
if (node == phc_node)
return(rc);
break;
}
#ifdef DEBUG
DPRINTF(("skip %d cells:",
address_cells + interrupt_cells +
pintr_cells + 1));
for (len = 0; len < (address_cells +
interrupt_cells + pintr_cells + 1); len++)
DPRINTF(("%x.", imap[len]));
#endif
i += address_cells + interrupt_cells + pintr_cells + 1;
}
if ((len = OF_getprop(node, "reg", ®, sizeof(reg))) <= 0) {
DPRINTF(("OF_mapintr: no reg property?\n"));
} else {
DPRINTF(("reg len %d\n", len));
}
if (free_map) {
free(free_map, M_DEVBUF);
free_map = NULL;
}
node = OF_parent(node);
}
return (rc);
}