/* $OpenBSD: ofw_machdep.c,v 1.28 2008/07/10 08:57:05 kettenis Exp $ */ /* $NetBSD: ofw_machdep.c,v 1.16 2001/07/20 00:07:14 eeh Exp $ */ /* * Copyright (C) 1996 Wolfgang Solfrank. * Copyright (C) 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(FFS) && defined(CD9660) #include #endif /* * Note that stdarg.h and the ANSI style va_start macro is used for both * ANSI and traditional C compilers. */ #include #include int vsprintf(char *, const char *, va_list); void dk_cleanup(void); 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() { u_int chosen; if ((chosen = OF_finddevice("/chosen")) == -1) { prom_printf("get_mmu_handle: cannot get /chosen\r\n"); return -1; } if (OF_getprop(chosen, "mmu", &mmuh, sizeof(mmuh)) == -1) { prom_printf("get_mmu_handle: cannot get mmuh\r\n"); return -1; } return mmuh; } static u_int get_memory_handle() { u_int chosen; if ((chosen = OF_finddevice("/chosen")) == -1) { prom_printf("get_memory_handle: cannot get /chosen\r\n"); return -1; } if (OF_getprop(chosen, "memory", &memh, sizeof(memh)) == -1) { prom_printf("get_memory_handle: cannot get memh\r\n"); return -1; } return memh; } /* * Point prom to our trap table. This stops the prom from mapping us. */ int prom_set_trap_table(tba, mmfsa) 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"); if (CPU_ISSUN4V) args.nargs = 2; else args.nargs = 1; args.nreturns = 0; args.tba = ADR2CELL(tba); args.mmfsa = ADR2CELL(mmfsa); return openfirmware(&args); } /* * Have the prom convert from virtual to physical addresses. * * Only works while the prom is actively mapping us. */ paddr_t prom_vtop(vaddr) 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\r\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,\r\n " "retaddr=%x %x, " "mode=%x %x, " "phys_hi=%x %x, " "phys_lo=%x %x\r\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); } /* * Grab some address space from the prom * * Only works while the prom is actively mapping us. */ vaddr_t prom_claim_virt(vaddr, len) 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\r\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 (paddr_t)args.retaddr; } /* * Request some address space from the prom * * Only works while the prom is actively mapping us. */ vaddr_t prom_alloc_virt(len, align) 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\r\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; } /* * Release some address space to the prom * * Only works while the prom is actively mapping us. */ int prom_free_virt(vaddr, len) 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\r\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); } /* * Unmap some address space * * Only works while the prom is actively mapping us. */ int prom_unmap_virt(vaddr, len) 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\r\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); } /* * Have prom map in some memory * * Only works while the prom is actively mapping us. */ int prom_map_phys(paddr, size, vaddr, mode) 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; cell_t status; cell_t retaddr; } args; if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) { prom_printf("prom_map_phys: cannot get mmuh\r\n"); return 0; } args.name = ADR2CELL("call-method"); args.nargs = 7; args.nreturns = 1; 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; if (args.status) return -1; return (int)args.retaddr; } /* * Request some RAM from the prom * * Only works while the prom is actively mapping us. */ paddr_t prom_alloc_phys(len, align) 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\r\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); } /* * Request some specific RAM from the prom * * Only works while the prom is actively mapping us. */ paddr_t prom_claim_phys(phys, len) 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\r\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); } /* * Free some RAM to prom * * Only works while the prom is actively mapping us. */ int prom_free_phys(phys, len) 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\r\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); } /* * Get the msgbuf from the prom. Only works once. * * Only works while the prom is actively mapping us. */ paddr_t prom_get_msgbuf(len, align) 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; int rooth; int is_e250 = 1; /* E250s tend to have buggy PROMs that break on test-method */ if ((rooth = OF_finddevice("/")) != -1) { char name[80]; if ((OF_getprop(rooth, "name", &name, sizeof(name))) != -1) { if (strcmp(name, "SUNW,Ultra-250") && strcmp(name, "SUNW,Ultra-4")) is_e250 = 0; } else prom_printf("prom_get_msgbuf: cannot get \"name\"\r\n"); } else prom_printf("prom_get_msgbuf: cannot open root device \r\n"); if (memh == -1 && ((memh = get_memory_handle()) == -1)) { prom_printf("prom_get_msgbuf: cannot get memh\r\n"); return -1; } if (is_e250) { prom_printf("prom_get_msgbuf: Cannot recover msgbuf on E250/450\r\n"); } else if (OF_test("test-method") == 0) { if (OF_test_method(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); prom_printf("prom_get_msgbuf: SUNW,retain failed\r\n"); } else prom_printf("prom_get_msgbuf: test-method failed\r\n"); } else prom_printf("prom_get_msgbuf: test failed\r\n"); /* Allocate random memory -- page zero avail?*/ addr = prom_claim_phys(0x000, len); prom_printf("prom_get_msgbuf: allocated new buf at %08x\r\n", (int)addr); if (addr == -1) { prom_printf("prom_get_msgbuf: cannot get allocate physmem\r\n"); return -1; } prom_printf("prom_get_msgbuf: claiming new buf at %08x\r\n", (int)addr); { int i; for (i=0; i<200000000; i++); } return addr; /* Kluge till we go 64-bit */ } int prom_itlb_load(int index, u_int64_t data, vaddr_t vaddr) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t method; cell_t ihandle; cell_t vaddr; cell_t data; cell_t index; cell_t status; } args; if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) { prom_printf("prom_itlb_load: cannot get mmuh\r\n"); return 0; } args.name = ADR2CELL("call-method"); args.nargs = 5; args.nreturns = 1; args.method = ADR2CELL("SUNW,itlb-load"); args.ihandle = HDL2CELL(mmuh); args.vaddr = ADR2CELL(vaddr); args.data = data; args.index = index; if(openfirmware(&args) == -1) return -1; if (args.status) return -1; return 0; } int prom_dtlb_load(int index, u_int64_t data, vaddr_t vaddr) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t method; cell_t ihandle; cell_t vaddr; cell_t data; cell_t index; cell_t status; } args; if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) { prom_printf("prom_itlb_load: cannot get mmuh\r\n"); return 0; } args.name = ADR2CELL("call-method"); args.nargs = 5; args.nreturns = 1; args.method = ADR2CELL("SUNW,dtlb-load"); args.ihandle = HDL2CELL(mmuh); args.vaddr = ADR2CELL(vaddr); args.data = data; args.index = index; if(openfirmware(&args) == -1) return -1; if (args.status) return -1; return 0; } #ifdef MULTIPROCESSOR /* * Start secondary cpu, arrange 'func' as the entry. */ void prom_start_cpu(int cpu, void *func, 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 = HDL2CELL(cpu); args.func = ADR2CELL(func); args.arg = arg; openfirmware(&args); } void prom_start_cpu_by_cpuid(int cpu, void *func, 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; args.name = ADR2CELL("SUNW,start-cpu-by-cpuid"); args.nargs = 3; args.nreturns = 1; args.cpu = cpu; args.func = ADR2CELL(func); args.arg = arg; openfirmware(&args); } #endif /* * Low-level prom I/O routines. */ static u_int stdin = 0; static u_int stdout = 0; int OF_stdin() { u_int chosen; if (stdin != 0) return stdin; chosen = OF_finddevice("/chosen"); OF_getprop(chosen, "stdin", &stdin, sizeof(stdin)); return stdin; } int OF_stdout() { u_int chosen; if (stdout != 0) return stdout; chosen = OF_finddevice("/chosen"); OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)); return stdout; } /* * print debug info to prom. * This is not safe, but then what do you expect? */ void prom_printf(const char *fmt, ...) { int len; static char buf[256]; va_list ap; va_start(ap, fmt); len = vsnprintf(buf, sizeof buf, fmt, ap); if (len == -1) len = 0; else if (len >= sizeof buf) len = sizeof buf - 1; va_end(ap); OF_write(OF_stdout(), buf, len); } const char * prom_serengeti_set_console_input(const char *new) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t new; cell_t old; } args; args.name = ADR2CELL("SUNW,set-console-input"); args.nargs = 1; args.nreturns = 1; args.new = ADR2CELL(new); if (openfirmware(&args) == -1) return NULL; return (const char *)args.old; } time_t prom_opl_get_tod(void) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t stick; cell_t time; } args; args.name = ADR2CELL("FJSV,get-tod"); args.nargs = 0; args.nreturns = 2; if (openfirmware(&args) == -1) return (time_t)-1; return (time_t)args.time; } #ifdef DEBUG int ofmapintrdebug = 0; #define DPRINTF(x) do { if (ofmapintrdebug) printf x; } while (0) #else #define DPRINTF(x) #endif /* * Recursively hunt for a property. */ int OF_searchprop(int node, char *prop, void *buf, int buflen) { int len; for( ; node; node = OF_parent(node)) { len = OF_getprop(node, prop, buf, buflen); if (len >= 0) return (len); } /* Error -- not found */ return (-1); } /* * Compare a sequence of cells with a mask, * return 1 if they match and 0 if they don't. */ 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 %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); } /* * Find top pci bus host controller for a node. */ 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") == 0 || strcmp(dev_type, "pciex") == 0) pch = node; } return pch; } /* * Follow the OFW algorithm and return an interrupt specifier. * * Pass in the interrupt specifier you want mapped and the node * you want it mapped from. validlen is the number of cells in * the interrupt specifier, and buflen is the number of cells in * the buffer. */ 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 interrupt_map[256]; int interrupt_map_mask[10]; int reg[10]; char dev_type[32]; int phc_node; int rc = -1; /* * Don't try to map interrupts for onboard devices, or if the * interrupt is already fully specified. */ if (*interrupt & 0x20 || *interrupt & 0x7c0) return validlen; /* * If there is no interrupt map in the bus node, we * need to convert the slot address to its parent * bus format, and hunt up the parent bus to see if * we need to remap. * * The specification for interrupt mapping is borken. * You are supposed to query the interrupt parent in * the interrupt-map specification to determine the * number of address and interrupt cells, but we need * to know how many address and interrupt cells to skip * to find the phandle... * */ if ((len = OF_getprop(node, "reg", ®, sizeof(reg))) <= 0) { printf("OF_mapintr: no reg property?\n"); return (-1); } phc_node = find_pci_host_node(node); 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 if ((interrupt_map_len = OF_getprop(node, "interrupt-map", &interrupt_map, sizeof(interrupt_map))) <= 0) { /* Swizzle interrupt if this is a PCI bridge. */ if (((len = OF_getprop(node, "device_type", &dev_type, sizeof(dev_type))) > 0) && (strcmp(dev_type, "pci") == 0 || strcmp(dev_type, "pciex") == 0) && (node != phc_node)) { *interrupt = ((*interrupt + OFW_PCI_PHYS_HI_DEVICE(reg[0]) - 1) & 3) + 1; DPRINTF(("OF_mapintr: interrupt %x, reg[0] %x\n", *interrupt, reg[0])); } /* Get reg for next level compare. */ reg[0] = 0; OF_getprop(node, "reg", ®, sizeof(reg)); node = OF_parent(node); continue; } /* Convert from bytes to cells. */ interrupt_map_len = interrupt_map_len/sizeof(int); if ((len = (OF_searchprop(node, "#address-cells", &address_cells, sizeof(address_cells)))) <= 0) { /* How should I know. */ 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) { /* How should I know. */ 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) { /* How should I know. */ 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) { /* Create a mask that masks nothing. */ 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 /* Finally we can attempt the compare. */ 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 (a %d, i %d p %p) ", address_cells, interrupt_cells, parent)); for (len=0; len