#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ofw.c,v 1.69 2023/12/10 23:19:12 andvar Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/reboot.h>
#include <sys/mbuf.h>
#include <sys/cpu.h>
#include <sys/intr.h>
#include <uvm/uvm.h>
#include <dev/cons.h>
#define _ARM32_BUS_DMA_PRIVATE
#include <sys/bus.h>
#include <arm/locore.h>
#include <machine/bootconfig.h>
#include <machine/irqhandler.h>
#include <dev/ofw/openfirm.h>
#include <machine/ofw.h>
#include <netinet/in.h>
#if BOOT_FW_DHCP
#include <nfs/bootdata.h>
#endif
#ifdef SHARK
#include "machine/pio.h"
#include "machine/isa_machdep.h"
#endif
#include "isadma.h"
#include "igsfb_ofbus.h"
#include "chipsfb_ofbus.h"
#include "vga_ofbus.h"
#define IO_VIRT_BASE (OFW_VIRT_BASE + OFW_VIRT_SIZE)
#define IO_VIRT_SIZE 0x01000000
#define KERNEL_IMG_PTS 2
#define KERNEL_VMDATA_PTS (KERNEL_VM_SIZE >> (L1_S_SHIFT + 2))
#define KERNEL_OFW_PTS 4
#define KERNEL_IO_PTS 4
#define KERNEL_VM_BASE (KERNEL_BASE + 0x01000000)
#define KERNEL_VM_SIZE 0x06000000
extern BootConfig bootconfig;
#ifdef DIAGNOSTIC
extern u_int current_mask;
#endif
extern int ofw_handleticks;
extern void dump_spl_masks(void);
extern void dumpsys(void);
extern void dotickgrovelling(vaddr_t);
#define WriteWord(a, b) \
*((volatile unsigned int *)(a)) = (b)
#define ReadWord(a) \
(*((volatile unsigned int *)(a)))
paddr_t physical_start;
paddr_t physical_freestart;
paddr_t physical_freeend;
paddr_t physical_end;
u_int free_pages;
paddr_t msgbufphys;
static vaddr_t virt_freeptr;
int ofw_callbacks = 0;
#if (NIGSFB_OFBUS > 0) || (NCHIPSFB_OFBUS > 0) || (NVGA_OFBUS > 0)
int console_ihandle = 0;
static void reset_screen(void);
#endif
struct mem_region {
paddr_t start;
psize_t size;
};
struct mem_translation {
vaddr_t virt;
vsize_t size;
paddr_t phys;
unsigned int mode;
};
struct isa_range {
paddr_t isa_phys_hi;
paddr_t isa_phys_lo;
paddr_t parent_phys_start;
psize_t isa_size;
};
struct vl_range {
paddr_t vl_phys_hi;
paddr_t vl_phys_lo;
paddr_t parent_phys_start;
psize_t vl_size;
};
struct vl_isa_range {
paddr_t isa_phys_hi;
paddr_t isa_phys_lo;
paddr_t parent_phys_hi;
paddr_t parent_phys_lo;
psize_t isa_size;
};
struct dma_range {
paddr_t start;
psize_t size;
};
struct ofw_cbargs {
char *name;
int nargs;
int nreturns;
int args_n_results[12];
};
static int nOFphysmem;
static struct mem_region *OFphysmem;
static int nOFphysavail;
static struct mem_region *OFphysavail;
static int nOFtranslations;
static struct mem_translation *OFtranslations;
static int nOFdmaranges;
static struct dma_range *OFdmaranges;
static ofw_handle_t ofw_client_services_handle;
static void ofw_callbackhandler(void *);
static void ofw_construct_proc0_addrspace(void);
static void ofw_getphysmeminfo(void);
static void ofw_getvirttranslations(void);
static void *ofw_malloc(vsize_t size);
static void ofw_claimpages(vaddr_t *, pv_addr_t *, vsize_t);
static void ofw_discardmappings(vaddr_t, vaddr_t, vsize_t);
static int ofw_mem_ihandle(void);
static int ofw_mmu_ihandle(void);
static paddr_t ofw_claimphys(paddr_t, psize_t, paddr_t);
#if 0
static paddr_t ofw_releasephys(paddr_t, psize_t);
#endif
static vaddr_t ofw_claimvirt(vaddr_t, vsize_t, vaddr_t);
static void ofw_settranslation(vaddr_t, paddr_t, vsize_t, int);
static void ofw_initallocator(void);
static void ofw_configisaonly(paddr_t *, paddr_t *);
static void ofw_configvl(int, paddr_t *, paddr_t *);
static vaddr_t ofw_valloc(vsize_t, vaddr_t);
int
openfirmware(void *args)
{
int ofw_result;
u_int saved_irq_state;
saved_irq_state = disable_interrupts(I32_bit);
ofw_result = ofw_client_services_handle(args);
(void)restore_interrupts(saved_irq_state);
return(ofw_result);
}
void
ofw_init(ofw_handle_t ofw_handle)
{
ofw_client_services_handle = ofw_handle;
virt_freeptr = KERNEL_BASE + (0x00400000 * KERNEL_IMG_PTS);
}
void
ofw_boot(int howto, char *bootstr)
{
#ifdef DIAGNOSTIC
printf("boot: howto=%08x curlwp=%p\n", howto, curlwp);
printf("current_mask=%08x\n", current_mask);
printf("ipl_bio=%08x ipl_net=%08x ipl_tty=%08x ipl_vm=%08x\n",
irqmasks[IPL_BIO], irqmasks[IPL_NET], irqmasks[IPL_TTY],
irqmasks[IPL_VM]);
printf("ipl_clock=%08x ipl_none=%08x\n",
irqmasks[IPL_CLOCK], irqmasks[IPL_NONE]);
dump_spl_masks();
#endif
if (cold) {
doshutdownhooks();
pmf_system_shutdown(boothowto);
printf("Halted while still in the ICE age.\n");
printf("The operating system has halted.\n");
goto ofw_exit;
}
if (!(howto & RB_NOSYNC))
bootsync();
splhigh();
if ((howto & (RB_DUMP | RB_HALT)) == RB_DUMP)
dumpsys();
doshutdownhooks();
pmf_system_shutdown(boothowto);
IRQdisable;
if (howto & RB_HALT) {
printf("The operating system has halted.\n");
goto ofw_exit;
}
printf("rebooting...\n");
{
static char str[256];
char *ap = str, *ap1 = ap;
if (bootstr && *bootstr) {
if (strlen(bootstr) > sizeof str - 5)
printf("boot string too large, ignored\n");
else {
strcpy(str, bootstr);
ap1 = ap = str + strlen(str);
*ap++ = ' ';
}
}
*ap++ = '-';
if (howto & RB_SINGLE)
*ap++ = 's';
if (howto & RB_KDB)
*ap++ = 'd';
*ap++ = 0;
if (ap[-2] == '-')
*ap1 = 0;
#if (NIGSFB_OFBUS > 0) || (NCHIPSFB_OFBUS > 0) || (NVGA_OFBUS > 0)
reset_screen();
#endif
OF_boot(str);
}
ofw_exit:
printf("Calling OF_exit...\n");
#if (NIGSFB_OFBUS > 0) || (NCHIPSFB_OFBUS > 0) || (NVGA_OFBUS > 0)
reset_screen();
#endif
OF_exit();
}
#if BOOT_FW_DHCP
extern char *ip2dotted(struct in_addr);
void
get_fw_dhcp_data(struct bootdata *bdp)
{
int chosen;
int dhcplen;
memset((char *)bdp, 0, sizeof(*bdp));
if ((chosen = OF_finddevice("/chosen")) == -1)
panic("no /chosen from OFW");
if ((dhcplen = OF_getproplen(chosen, "bootp-response")) > 0) {
u_char *cp;
int dhcp_type = 0;
char *ip;
if (dhcplen > sizeof(bdp->dhcp_packet))
panic("DHCP packet too large");
OF_getprop(chosen, "bootp-response", &bdp->dhcp_packet,
sizeof(bdp->dhcp_packet));
SANITY(bdp->dhcp_packet.op == BOOTREPLY, "bogus DHCP packet");
bdp->ip_address = bdp->dhcp_packet.yiaddr;
ip = ip2dotted(bdp->ip_address);
if (memcmp(bdp->dhcp_packet.options, DHCP_OPTIONS_COOKIE, 4) == 0)
parse_dhcp_options(&bdp->dhcp_packet,
bdp->dhcp_packet.options + 4,
&bdp->dhcp_packet.options[dhcplen
- DHCP_FIXED_NON_UDP], bdp, ip);
if (bdp->root_ip.s_addr == 0)
bdp->root_ip = bdp->dhcp_packet.siaddr;
if (bdp->swap_ip.s_addr == 0)
bdp->swap_ip = bdp->dhcp_packet.siaddr;
}
{
int options;
int proplen;
#define BOOTJUNKV_SIZE 256
char bootjunkv[BOOTJUNKV_SIZE];
if ((options = OF_finddevice("/options")) == -1)
panic("can't find /options");
if (bdp->ip_address.s_addr == 0 &&
(proplen = OF_getprop(options, "ipaddr",
bootjunkv, BOOTJUNKV_SIZE - 1)) > 0) {
bootjunkv[proplen] = '\0';
if (dotted2ip(bootjunkv, &bdp->ip_address.s_addr) == 0)
bdp->ip_address.s_addr = 0;
}
if (bdp->ip_mask.s_addr == 0 &&
(proplen = OF_getprop(options, "netmask",
bootjunkv, BOOTJUNKV_SIZE - 1)) > 0) {
bootjunkv[proplen] = '\0';
if (dotted2ip(bootjunkv, &bdp->ip_mask.s_addr) == 0)
bdp->ip_mask.s_addr = 0;
}
if (bdp->hostname[0] == '\0' &&
(proplen = OF_getprop(options, "hostname",
bdp->hostname, sizeof(bdp->hostname) - 1)) > 0) {
bdp->hostname[proplen] = '\0';
}
if (bdp->root[0] == '\0' &&
(proplen = OF_getprop(options, "rootfs",
bootjunkv, BOOTJUNKV_SIZE - 1)) > 0) {
bootjunkv[proplen] = '\0';
parse_server_path(bootjunkv, &bdp->root_ip, bdp->root);
}
if (bdp->swap[0] == '\0' &&
(proplen = OF_getprop(options, "swapfs",
bootjunkv, BOOTJUNKV_SIZE - 1)) > 0) {
bootjunkv[proplen] = '\0';
parse_server_path(bootjunkv, &bdp->swap_ip, bdp->swap);
}
}
}
#endif
void
ofw_getbootinfo(char **bp_pp, char **ba_pp)
{
int chosen;
int bp_len;
int ba_len;
char *bootpathv;
char *bootargsv;
if ((chosen = OF_finddevice("/chosen")) == -1)
panic("no /chosen from OFW");
bp_len = OF_getproplen(chosen, "bootpath");
ba_len = OF_getproplen(chosen, "bootargs");
if (bp_len < 0 || ba_len < 0)
panic("can't get boot data from OFW");
bootpathv = (char *)ofw_malloc(bp_len);
bootargsv = (char *)ofw_malloc(ba_len);
if (bp_len)
OF_getprop(chosen, "bootpath", bootpathv, bp_len);
else
bootpathv[0] = '\0';
if (ba_len)
OF_getprop(chosen, "bootargs", bootargsv, ba_len);
else
bootargsv[0] = '\0';
*bp_pp = bootpathv;
*ba_pp = bootargsv;
#ifdef DIAGNOSTIC
printf("bootpath=<%s>, bootargs=<%s>\n", bootpathv, bootargsv);
#endif
}
paddr_t
ofw_getcleaninfo(void)
{
int cpu;
vaddr_t vclean;
paddr_t pclean;
if ((cpu = OF_finddevice("/cpu")) == -1)
panic("no /cpu from OFW");
if ((OF_getprop(cpu, "d-cache-flush-address", &vclean,
sizeof(vclean))) != sizeof(vclean)) {
#ifdef DEBUG
printf("no OFW d-cache-flush-address property\n");
#endif
return -1;
}
if ((pclean = ofw_gettranslation(
of_decode_int((unsigned char *)&vclean))) == -1)
panic("OFW failed to translate cache flush address");
return pclean;
}
void
ofw_configisa(paddr_t *pio, paddr_t *pmem)
{
int vl;
if ((vl = OF_finddevice("/vlbus")) == -1)
ofw_configisaonly(pio, pmem);
else
ofw_configvl(vl, pio, pmem);
}
static void
ofw_configisaonly(paddr_t *pio, paddr_t *pmem)
{
int isa;
int rangeidx;
int size;
paddr_t hi, start;
struct isa_range ranges[2];
if ((isa = OF_finddevice("/isa")) == -1)
panic("OFW has no /isa device node");
if ((size = OF_getprop(isa, "ranges", ranges, sizeof(ranges)))
!= sizeof(ranges))
panic("unexpected size of OFW /isa ranges property: %d", size);
*pio = *pmem = -1;
for (rangeidx = 0; rangeidx < 2; ++rangeidx) {
hi = of_decode_int((unsigned char *)
&ranges[rangeidx].isa_phys_hi);
start = of_decode_int((unsigned char *)
&ranges[rangeidx].parent_phys_start);
if (hi & 1) {
*pio = start;
} else {
*pmem = start;
}
}
if ((*pio == -1) || (*pmem == -1))
panic("bad OFW /isa ranges property");
}
static void
ofw_configvl(int vl, paddr_t *pio, paddr_t *pmem)
{
int isa;
int ir, vr;
int size;
paddr_t hi, start;
struct vl_isa_range isa_ranges[2];
struct vl_range vl_ranges[2];
if ((isa = OF_finddevice("/vlbus/isa")) == -1)
panic("OFW has no /vlbus/isa device node");
if ((size = OF_getprop(isa, "ranges", isa_ranges, sizeof(isa_ranges)))
!= sizeof(isa_ranges))
panic("unexpected size of OFW /vlbus/isa ranges property: %d",
size);
if ((size = OF_getprop(vl, "ranges", vl_ranges, sizeof(vl_ranges)))
!= sizeof(vl_ranges))
panic("unexpected size of OFW /vlbus ranges property: %d", size);
*pio = -1;
*pmem = -1;
for (ir = 0; ir < 2; ++ir) {
for (vr = 0; vr < 2; ++vr) {
if ((isa_ranges[ir].parent_phys_hi
== vl_ranges[vr].vl_phys_hi) &&
(isa_ranges[ir].parent_phys_lo
== vl_ranges[vr].vl_phys_lo)) {
hi = of_decode_int((unsigned char *)
&isa_ranges[ir].isa_phys_hi);
start = of_decode_int((unsigned char *)
&vl_ranges[vr].parent_phys_start);
if (hi & 1) {
*pio = start;
} else {
*pmem = start;
}
}
}
}
if ((*pio == -1) || (*pmem == -1))
panic("bad OFW /isa ranges property");
}
#if NISADMA > 0
struct arm32_dma_range *shark_isa_dma_ranges;
int shark_isa_dma_nranges;
#endif
void
ofw_configisadma(paddr_t *pdma)
{
int root;
int rangeidx;
int size;
struct dma_range *dr;
if ((root = OF_finddevice("/")) == -1 ||
(size = OF_getproplen(root, "dma-ranges")) <= 0 ||
(OFdmaranges = (struct dma_range *)ofw_malloc(size)) == 0 ||
OF_getprop(root, "dma-ranges", OFdmaranges, size) != size)
panic("bad / dma-ranges property");
nOFdmaranges = size / sizeof(struct dma_range);
#if NISADMA > 0
shark_isa_dma_ranges = ofw_malloc(nOFdmaranges *
sizeof(*shark_isa_dma_ranges));
if (shark_isa_dma_ranges == NULL)
panic("unable to allocate shark_isa_dma_ranges");
shark_isa_dma_nranges = nOFdmaranges;
#endif
for (rangeidx = 0, dr = OFdmaranges; rangeidx < nOFdmaranges;
++rangeidx, ++dr) {
dr->start = of_decode_int((unsigned char *)&dr->start);
dr->size = of_decode_int((unsigned char *)&dr->size);
#if NISADMA > 0
shark_isa_dma_ranges[rangeidx].dr_sysbase = dr->start;
shark_isa_dma_ranges[rangeidx].dr_busbase = dr->start;
shark_isa_dma_ranges[rangeidx].dr_len = dr->size;
#endif
}
#ifdef DEBUG
printf("DMA ranges size = %d\n", size);
for (rangeidx = 0; rangeidx < nOFdmaranges; ++rangeidx) {
printf("%08lx %08lx\n",
(u_long)OFdmaranges[rangeidx].start,
(u_long)OFdmaranges[rangeidx].size);
}
#endif
}
void
ofw_configmem(void)
{
int i;
ofw_construct_proc0_addrspace();
ofw_getphysmeminfo();
ofw_initallocator();
OF_set_callback(ofw_callbackhandler);
cpu_domains((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT);
cpu_setttb(kernel_l1pt.pv_pa, true);
cpu_tlb_flushID();
cpu_domains(DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2));
uvm_lwp_setuarea(&lwp0, kernelstack.pv_va);
{
struct mem_region *mp;
int totalcnt;
int availcnt;
physmem = 0;
for (totalcnt = 0, mp = OFphysmem; totalcnt < nOFphysmem;
totalcnt++, mp++) {
#ifdef OLDPRINTFS
printf("physmem: %lx, %lx\n", mp->start, mp->size);
#endif
physmem += btoc(mp->size);
}
physical_start = OFphysmem[0].start;
mp--;
physical_end = mp->start + mp->size;
free_pages = 0;
for (availcnt = 0, mp = OFphysavail; availcnt < nOFphysavail;
availcnt++, mp++) {
#ifdef OLDPRINTFS
printf("physavail: %lx, %lx\n", mp->start, mp->size);
#endif
free_pages += btoc(mp->size);
}
physical_freestart = OFphysavail[0].start;
mp--;
physical_freeend = mp->start + mp->size;
#ifdef OLDPRINTFS
printf("pmap_bootstrap: physmem = %lx, free_pages = %x\n",
physmem, free_pages);
#endif
if (DRAM_BLOCKS < (availcnt + 1))
panic("more ofw memory regions than bootconfig blocks");
for (i = 0, mp = OFphysavail; i < nOFphysavail; i++, mp++) {
bootconfig.dram[i].address = mp->start;
bootconfig.dram[i].pages = btoc(mp->size);
}
bootconfig.dramblocks = availcnt;
}
uvm_md_init();
for (i = 0; i < bootconfig.dramblocks; i++) {
paddr_t start = (paddr_t)bootconfig.dram[i].address;
paddr_t end = start + (bootconfig.dram[i].pages * PAGE_SIZE);
#if NISADMA > 0
paddr_t istart, isize;
#endif
if (start < physical_freestart)
start = physical_freestart;
if (end > physical_freeend)
end = physical_freeend;
#if 0
printf("%d: %lx -> %lx\n", loop, start, end - 1);
#endif
#if NISADMA > 0
if (arm32_dma_range_intersect(shark_isa_dma_ranges,
shark_isa_dma_nranges,
start, end - start,
&istart, &isize)) {
#if 0
printf(" ISADMA 0x%lx -> 0x%lx\n", istart,
istart + isize - 1);
#endif
uvm_page_physload(atop(istart),
atop(istart + isize), atop(istart),
atop(istart + isize), VM_FREELIST_ISADMA);
if (start < istart) {
#if 0
printf(" BEFORE 0x%lx -> 0x%lx\n",
start, istart - 1);
#endif
uvm_page_physload(atop(start),
atop(istart), atop(start),
atop(istart), VM_FREELIST_DEFAULT);
}
if ((istart + isize) < end) {
#if 0
printf(" AFTER 0x%lx -> 0x%lx\n",
(istart + isize), end - 1);
#endif
uvm_page_physload(atop(istart + isize),
atop(end), atop(istart + isize),
atop(end), VM_FREELIST_DEFAULT);
}
} else {
uvm_page_physload(atop(start), atop(end),
atop(start), atop(end), VM_FREELIST_DEFAULT);
}
#else
uvm_page_physload(atop(start), atop(end),
atop(start), atop(end), VM_FREELIST_DEFAULT);
#endif
}
pmap_bootstrap(KERNEL_VM_BASE, KERNEL_VM_BASE + KERNEL_VM_SIZE);
}
static void
ofw_callbackhandler(void *v)
{
struct ofw_cbargs *args = v;
char *name = args->name;
int nargs = args->nargs;
int nreturns = args->nreturns;
int *args_n_results = args->args_n_results;
ofw_callbacks++;
#if defined(OFWGENCFG)
if (strcmp(name, "tick") == 0) {
vaddr_t frame;
if (nargs != 1 || nreturns < 1) {
args_n_results[nargs] = -1;
args->nreturns = 1;
return;
}
args_n_results[nargs] = 0;
frame = (vaddr_t)args_n_results[0];
if (ofw_handleticks)
dotickgrovelling(frame);
args_n_results[nargs + 1] = frame;
args->nreturns = 1;
} else
#endif
if (strcmp(name, "map") == 0) {
vaddr_t va;
paddr_t pa;
vsize_t size;
int mode;
int ap_bits;
int dom_bits;
int cb_bits;
if (nargs != 4 || nreturns < 2) {
args_n_results[nargs] = -1;
args->nreturns = 1;
return;
}
args_n_results[nargs] = 0;
pa = (paddr_t)args_n_results[0];
va = (vaddr_t)args_n_results[1];
size = (vsize_t)args_n_results[2];
mode = args_n_results[3];
ap_bits = (mode & 0x00000C00);
dom_bits = (mode & 0x000001E0);
cb_bits = (mode & 0x000000C0);
if ((va & PGOFSET) != 0 || va < OFW_VIRT_BASE ||
(va + size) > (OFW_VIRT_BASE + OFW_VIRT_SIZE) ||
(pa & PGOFSET) != 0 || (size & PGOFSET) != 0 ||
size == 0 || (dom_bits >> 5) != 0) {
args_n_results[nargs + 1] = -1;
args->nreturns = 1;
return;
}
cpu_idcache_wbinv_all();
{
pt_entry_t *ptep = vtopte(va);
KASSERT(ptep + size / L2_S_SIZE == vtopte(va + size));
pt_entry_t npte = pa | L2_TYPE_S | L2_AP(ap_bits)
| cb_bits;
ap_bits >>= 10;
for (size_t npages = size >> PGSHIFT;
npages-- > 0;
ptep += PAGE_SIZE / L2_S_SIZE, npte += PAGE_SIZE) {
l2pte_set(ptep, npte, 0);
}
PTE_SYNC_RANGE(vtopte(va), size >> L2_S_SHIFT);
}
cpu_tlb_flushID();
args_n_results[nargs + 1] = 0;
args->nreturns = 2;
} else if (strcmp(name, "unmap") == 0) {
vaddr_t va;
vsize_t size;
if (nargs != 2 || nreturns < 1) {
args_n_results[nargs] = -1;
args->nreturns = 1;
return;
}
args_n_results[nargs] = 0;
va = (vaddr_t)args_n_results[0];
size = (vsize_t)args_n_results[1];
if ((va & PGOFSET) != 0 || va < OFW_VIRT_BASE ||
(va + size) > (OFW_VIRT_BASE + OFW_VIRT_SIZE) ||
(size & PGOFSET) != 0 || size == 0) {
args_n_results[nargs + 1] = -1;
args->nreturns = 1;
return;
}
cpu_idcache_wbinv_all();
{
pt_entry_t *ptep = vtopte(va);
for (size_t npages = size >> PGSHIFT;
npages-- > 0;
ptep += PAGE_SIZE / L2_S_SIZE) {
l2pte_reset(ptep);
}
PTE_SYNC_RANGE(vtopte(va), size >> L2_S_SHIFT);
}
cpu_tlb_flushID();
args->nreturns = 1;
} else if (strcmp(name, "translate") == 0) {
vaddr_t va;
paddr_t pa;
int mode;
pt_entry_t pte;
if (nargs != 1 || nreturns < 4) {
args_n_results[nargs] = -1;
args->nreturns = 1;
return;
}
args_n_results[nargs] = 0;
va = (vaddr_t)args_n_results[0];
if ((va & PGOFSET) != 0 || va < OFW_VIRT_BASE ||
va >= (OFW_VIRT_BASE + OFW_VIRT_SIZE)) {
args_n_results[nargs + 1] = -1;
args->nreturns = 1;
return;
}
pte = *vtopte(va);
if (pte == 0) {
args_n_results[nargs + 1] = -1;
args->nreturns = 2;
} else {
pa = (pte & L2_S_FRAME) | (va & L2_S_OFFSET);
mode = (pte & 0x0C00) | (0 << 5) | (pte & 0x000C);
args_n_results[nargs + 1] = 0;
args_n_results[nargs + 2] = pa;
args_n_results[nargs + 3] = mode;
args->nreturns = 4;
}
} else if (strcmp(name, "claim-phys") == 0) {
struct pglist alloclist;
paddr_t low, high, align;
psize_t size;
if (nargs != 4 || nreturns < 3) {
args_n_results[nargs] = -1;
args->nreturns = 1;
return;
}
args_n_results[nargs] = 0;
low = args_n_results[0];
size = args_n_results[2];
align = args_n_results[3];
high = args_n_results[1] + size;
#if 0
printf("claim-phys: low = 0x%x, size = 0x%x, align = 0x%x, high = 0x%x\n",
low, size, align, high);
align = size;
printf("forcing align to be 0x%x\n", align);
#endif
args_n_results[nargs + 1] =
uvm_pglistalloc(size, low, high, align, 0, &alloclist, 1, 0);
#if 0
printf(" -> 0x%lx", args_n_results[nargs + 1]);
#endif
if (args_n_results[nargs + 1] != 0) {
#if 0
printf("(failed)\n");
#endif
args_n_results[nargs + 1] = -1;
args->nreturns = 2;
return;
}
args_n_results[nargs + 2] = VM_PAGE_TO_PHYS(alloclist.tqh_first);
#if 0
printf("(succeeded: pa = 0x%lx)\n", args_n_results[nargs + 2]);
#endif
args->nreturns = 3;
} else if (strcmp(name, "release-phys") == 0) {
printf("unimplemented ofw callback - %s\n", name);
args_n_results[nargs] = -1;
args->nreturns = 1;
} else if (strcmp(name, "claim-virt") == 0) {
vaddr_t va;
vaddr_t align;
args_n_results[nargs] = -1;
args->nreturns = 1;
return;
if (nargs != 2 || nreturns < 3) {
args_n_results[nargs] = -1;
args->nreturns = 1;
return;
}
args_n_results[nargs] = 0;
align = (vaddr_t)args_n_results[1];
if (align % PAGE_SIZE != 0) {
args_n_results[nargs + 1] = -1;
args->nreturns = 2;
return;
}
if (va == 0) {
args_n_results[nargs + 1] = -1;
args->nreturns = 2;
} else {
args_n_results[nargs + 1] = 0;
args_n_results[nargs + 2] = va;
args->nreturns = 3;
}
} else if (strcmp(name, "release-virt") == 0) {
printf("unimplemented ofw callback - %s\n", name);
args_n_results[nargs] = -1;
args->nreturns = 1;
return;
if (nargs != 2 || nreturns < 1) {
args_n_results[nargs] = -1;
args->nreturns = 1;
return;
}
args_n_results[nargs] = 0;
args->nreturns = 1;
} else {
args_n_results[nargs] = -1;
args->nreturns = 1;
}
}
static void
ofw_construct_proc0_addrspace(void)
{
int i, oft;
static pv_addr_t proc0_pt_sys;
static pv_addr_t proc0_pt_kernel[KERNEL_IMG_PTS];
static pv_addr_t proc0_pt_vmdata[KERNEL_VMDATA_PTS];
static pv_addr_t proc0_pt_ofw[KERNEL_OFW_PTS];
static pv_addr_t proc0_pt_io[KERNEL_IO_PTS];
static pv_addr_t msgbuf;
vaddr_t L1pagetable;
struct mem_translation *tp;
KASSERT(vector_page == 0);
systempage.pv_va = ofw_claimvirt(vector_page, PAGE_SIZE, 0);
if (systempage.pv_va == -1) {
systempage.pv_va = vector_page;
systempage.pv_pa = ofw_gettranslation(vector_page);
if (systempage.pv_pa == -1)
panic("bogus result from gettranslation(vector_page)");
} else {
if (systempage.pv_va != vector_page)
panic("bogus result from claimvirt(vector_page, PAGE_SIZE, 0)");
systempage.pv_pa = ofw_claimphys(0, PAGE_SIZE, PAGE_SIZE);
if (systempage.pv_pa == -1)
panic("bogus result from claimphys(0, PAGE_SIZE, PAGE_SIZE)");
ofw_settranslation(systempage.pv_va, systempage.pv_pa,
PAGE_SIZE, -1);
memset((char *)systempage.pv_va, 0, PAGE_SIZE);
}
ofw_claimpages(&virt_freeptr, &kernel_l1pt, L1_TABLE_SIZE);
ofw_claimpages(&virt_freeptr, &proc0_pt_sys, L2_TABLE_SIZE);
for (i = 0; i < KERNEL_IMG_PTS; i++)
ofw_claimpages(&virt_freeptr, &proc0_pt_kernel[i], L2_TABLE_SIZE);
for (i = 0; i < KERNEL_VMDATA_PTS; i++)
ofw_claimpages(&virt_freeptr, &proc0_pt_vmdata[i], L2_TABLE_SIZE);
for (i = 0; i < KERNEL_OFW_PTS; i++)
ofw_claimpages(&virt_freeptr, &proc0_pt_ofw[i], L2_TABLE_SIZE);
for (i = 0; i < KERNEL_IO_PTS; i++)
ofw_claimpages(&virt_freeptr, &proc0_pt_io[i], L2_TABLE_SIZE);
#ifndef OFWGENCFG
ofw_claimpages(&virt_freeptr, &irqstack, PAGE_SIZE);
#endif
ofw_claimpages(&virt_freeptr, &undstack, PAGE_SIZE);
ofw_claimpages(&virt_freeptr, &abtstack, PAGE_SIZE);
ofw_claimpages(&virt_freeptr, &kernelstack, UPAGES * PAGE_SIZE);
ofw_claimpages(&virt_freeptr, &msgbuf, MSGBUFSIZE);
msgbufphys = msgbuf.pv_pa;
L1pagetable = kernel_l1pt.pv_va;
pmap_link_l2pt(L1pagetable, 0x0, &proc0_pt_sys);
for (i = 0; i < KERNEL_IMG_PTS; i++)
pmap_link_l2pt(L1pagetable, KERNEL_BASE + i * 0x00400000,
&proc0_pt_kernel[i]);
for (i = 0; i < KERNEL_VMDATA_PTS; i++)
pmap_link_l2pt(L1pagetable, KERNEL_VM_BASE + i * 0x00400000,
&proc0_pt_vmdata[i]);
for (i = 0; i < KERNEL_OFW_PTS; i++)
pmap_link_l2pt(L1pagetable, OFW_VIRT_BASE + i * 0x00400000,
&proc0_pt_ofw[i]);
for (i = 0; i < KERNEL_IO_PTS; i++)
pmap_link_l2pt(L1pagetable, IO_VIRT_BASE + i * 0x00400000,
&proc0_pt_io[i]);
ofw_getvirttranslations();
for (oft = 0, tp = OFtranslations; oft < nOFtranslations;
oft++, tp++) {
vaddr_t va;
paddr_t pa;
int npages = tp->size / PAGE_SIZE;
if (npages == 0 || tp->size % PAGE_SIZE != 0)
panic("illegal ofw translation (size)");
for (va = tp->virt, pa = tp->phys; npages > 0;
va += PAGE_SIZE, pa += PAGE_SIZE, npages--) {
switch (va >> (L1_S_SHIFT + 2)) {
case 0:
break;
#if KERNEL_IMG_PTS != 2
#error "Update ofw translation range list"
#endif
case ( KERNEL_BASE >> (L1_S_SHIFT + 2)):
case ((KERNEL_BASE + 0x00400000) >> (L1_S_SHIFT + 2)):
break;
case ( OFW_VIRT_BASE >> (L1_S_SHIFT + 2)):
case ((OFW_VIRT_BASE + 0x00400000) >> (L1_S_SHIFT + 2)):
case ((OFW_VIRT_BASE + 0x00800000) >> (L1_S_SHIFT + 2)):
case ((OFW_VIRT_BASE + 0x00C00000) >> (L1_S_SHIFT + 2)):
break;
case ( IO_VIRT_BASE >> (L1_S_SHIFT + 2)):
case ((IO_VIRT_BASE + 0x00400000) >> (L1_S_SHIFT + 2)):
case ((IO_VIRT_BASE + 0x00800000) >> (L1_S_SHIFT + 2)):
case ((IO_VIRT_BASE + 0x00C00000) >> (L1_S_SHIFT + 2)):
break;
default:
panic("illegal ofw translation (addr) %#lx",
va);
}
pmap_map_entry(L1pagetable, va, pa,
VM_PROT_READ|VM_PROT_WRITE,
(tp->mode & 0xC) == 0xC ? PTE_CACHE
: PTE_NOCACHE);
}
}
ofw_discardmappings(proc0_pt_kernel[KERNEL_IMG_PTS - 1].pv_va,
msgbuf.pv_va, MSGBUFSIZE);
pmap_curmaxkvaddr =
KERNEL_VM_BASE + (KERNEL_VMDATA_PTS * 0x00400000);
for (oft = 0, tp = OFtranslations; oft < nOFtranslations; oft++, tp++) {
vaddr_t va = tp->virt;
paddr_t pa = tp->phys;
if (((va | pa) & L1_S_OFFSET) == 0) {
int nsections = tp->size / L1_S_SIZE;
while (nsections--) {
pmap_map_section(L1pagetable, va, pa,
VM_PROT_READ|VM_PROT_WRITE,
(tp->mode & 0xC) == 0xC ? PTE_CACHE
: PTE_NOCACHE);
va += L1_S_SIZE;
pa += L1_S_SIZE;
}
}
}
}
static void
ofw_getphysmeminfo(void)
{
int phandle;
int mem_len;
int avail_len;
int i;
if ((phandle = OF_finddevice("/memory")) == -1 ||
(mem_len = OF_getproplen(phandle, "reg")) <= 0 ||
(OFphysmem = (struct mem_region *)ofw_malloc(mem_len)) == 0 ||
OF_getprop(phandle, "reg", OFphysmem, mem_len) != mem_len ||
(avail_len = OF_getproplen(phandle, "available")) <= 0 ||
(OFphysavail = (struct mem_region *)ofw_malloc(avail_len)) == 0 ||
OF_getprop(phandle, "available", OFphysavail, avail_len)
!= avail_len)
panic("can't get physmeminfo from OFW");
nOFphysmem = mem_len / sizeof(struct mem_region);
nOFphysavail = avail_len / sizeof(struct mem_region);
for (i = 0; i < 2; i++) {
struct mem_region *tmp = (i == 0) ? OFphysmem : OFphysavail;
struct mem_region *mp;
int cnt = (i == 0) ? nOFphysmem : nOFphysavail;
int j;
#ifdef OLDPRINTFS
printf("ofw_getphysmeminfo: %d blocks\n", cnt);
#endif
for (j = 0, mp = tmp; j < cnt; j++, mp++) {
mp->start = of_decode_int((unsigned char *)&mp->start);
mp->size = of_decode_int((unsigned char *)&mp->size);
}
for (j = 0, mp = tmp; j < cnt; j++, mp++) {
u_int s, sz;
struct mem_region *mp1;
s = mp->start % PAGE_SIZE;
if (s != 0) {
s = (PAGE_SIZE - s);
if (mp->size >= s) {
mp->start += s;
mp->size -= s;
}
}
mp->size -= mp->size % PAGE_SIZE;
if (mp->size == 0) {
memmove(mp, mp + 1, (cnt - (mp - tmp))
* sizeof(struct mem_region));
cnt--;
mp--;
continue;
}
s = mp->start;
sz = mp->size;
for (mp1 = tmp; mp1 < mp; mp1++)
if (s < mp1->start)
break;
if (mp1 < mp) {
memmove(mp1 + 1, mp1, (char *)mp - (char *)mp1);
mp1->start = s;
mp1->size = sz;
}
}
#ifdef OLDPRINTFS
for (mp = tmp; mp->size; mp++) {
printf("%lx, %lx\n", mp->start, mp->size);
}
#endif
}
}
static void
ofw_getvirttranslations(void)
{
int mmu_phandle;
int mmu_ihandle;
int trans_len;
int over, len;
int i;
struct mem_translation *tp;
mmu_ihandle = ofw_mmu_ihandle();
over = 4 * sizeof(struct mem_translation);
if ((mmu_phandle = OF_instance_to_package(mmu_ihandle)) == -1 ||
(len = OF_getproplen(mmu_phandle, "translations")) <= 0 ||
(OFtranslations = ofw_malloc(len + over)) == 0 ||
(trans_len = OF_getprop(mmu_phandle, "translations",
OFtranslations, len + over)) > (len + over))
panic("can't get virttranslations from OFW");
nOFtranslations = trans_len / sizeof(struct mem_translation);
#ifdef OLDPRINTFS
printf("ofw_getvirtmeminfo: %d blocks\n", nOFtranslations);
#endif
for (i = 0, tp = OFtranslations; i < nOFtranslations; i++, tp++) {
tp->virt = of_decode_int((unsigned char *)&tp->virt);
tp->size = of_decode_int((unsigned char *)&tp->size);
tp->phys = of_decode_int((unsigned char *)&tp->phys);
tp->mode = of_decode_int((unsigned char *)&tp->mode);
}
}
typedef struct _vfree {
struct _vfree *pNext;
vaddr_t start;
vsize_t size;
} VFREE, *PVFREE;
static VFREE vfinitial = { NULL, IO_VIRT_BASE, IO_VIRT_SIZE };
static PVFREE vflist = &vfinitial;
static vaddr_t
ofw_valloc(vsize_t size, vaddr_t align)
{
PVFREE *ppvf;
PVFREE pNew;
vaddr_t new;
vaddr_t lead;
for (ppvf = &vflist; *ppvf; ppvf = &((*ppvf)->pNext)) {
if (align == 0) {
new = (*ppvf)->start;
lead = 0;
} else {
new = ((*ppvf)->start + (align - 1)) & ~(align - 1);
lead = new - (*ppvf)->start;
}
if (((*ppvf)->size - lead) >= size) {
if (lead == 0) {
if (size == (*ppvf)->size) {
(*ppvf) = (*ppvf)->pNext;
} else {
(*ppvf)->start = new + size;
(*ppvf)->size -= size;
}
} else {
vsize_t tail = ((*ppvf)->start
+ (*ppvf)->size) - (new + size);
(*ppvf)->size = lead;
if (tail != 0) {
pNew = ofw_malloc(sizeof(VFREE));
pNew->pNext = (*ppvf)->pNext;
(*ppvf)->pNext = pNew;
pNew->start = new + size;
pNew->size = tail;
}
}
return new;
}
}
return -1;
}
vaddr_t
ofw_map(paddr_t pa, vsize_t size, int cb_bits)
{
vaddr_t va;
if ((va = ofw_valloc(size, size)) == -1)
panic("cannot alloc virtual memory for %#lx", pa);
ofw_claimvirt(va, size, 0);
ofw_settranslation(va, pa, size, L2_AP(AP_KRW) | cb_bits);
return va;
}
static int
ofw_mem_ihandle(void)
{
static int mem_ihandle = 0;
int chosen;
if (mem_ihandle != 0)
return(mem_ihandle);
if ((chosen = OF_finddevice("/chosen")) == -1 ||
OF_getprop(chosen, "memory", &mem_ihandle, sizeof(int)) < 0)
panic("ofw_mem_ihandle");
mem_ihandle = of_decode_int((unsigned char *)&mem_ihandle);
return(mem_ihandle);
}
static int
ofw_mmu_ihandle(void)
{
static int mmu_ihandle = 0;
int chosen;
if (mmu_ihandle != 0)
return(mmu_ihandle);
if ((chosen = OF_finddevice("/chosen")) == -1 ||
OF_getprop(chosen, "mmu", &mmu_ihandle, sizeof(int)) < 0)
panic("ofw_mmu_ihandle");
mmu_ihandle = of_decode_int((unsigned char *)&mmu_ihandle);
return(mmu_ihandle);
}
static paddr_t
ofw_claimphys(paddr_t pa, psize_t size, paddr_t align)
{
int mem_ihandle = ofw_mem_ihandle();
if (align == 0) {
pa = OF_call_method_1("claim", mem_ihandle, 3, pa, size, align);
} else {
pa = OF_call_method_1("claim", mem_ihandle, 2, size, align);
}
return(pa);
}
#if 0
static paddr_t
ofw_releasephys(paddr_t pa, psize_t size)
{
int mem_ihandle = ofw_mem_ihandle();
return (OF_call_method_1("release", mem_ihandle, 2, pa, size));
}
#endif
static vaddr_t
ofw_claimvirt(vaddr_t va, vsize_t size, vaddr_t align)
{
int mmu_ihandle = ofw_mmu_ihandle();
if (align == 0) {
va = OF_call_method_1("claim", mmu_ihandle, 3, va, size, align);
} else {
va = OF_call_method_1("claim", mmu_ihandle, 2, size, align);
}
return(va);
}
paddr_t
ofw_gettranslation(vaddr_t va)
{
int mmu_ihandle = ofw_mmu_ihandle();
paddr_t pa;
int mode;
int exists;
#ifdef OFW_DEBUG
printf("ofw_gettranslation (%x) --> ", (uint32_t)va);
#endif
exists = 0;
if (OF_call_method("translate", mmu_ihandle, 1, 3, va, &pa, &mode,
&exists) != 0) {
#ifdef OFW_DEBUG
printf("(failed)\n");
#endif
return(-1);
}
#ifdef OFW_DEBUG
printf("%d %x\n", exists, (uint32_t)pa);
#endif
return(exists ? pa : -1);
}
static void
ofw_settranslation(vaddr_t va, paddr_t pa, vsize_t size, int mode)
{
int mmu_ihandle = ofw_mmu_ihandle();
#ifdef OFW_DEBUG
printf("ofw_settranslation (%x, %x, %x, %x) --> void\n", (uint32_t)va,
(uint32_t)pa, (uint32_t)size, (uint32_t)mode);
#endif
if (OF_call_method("map", mmu_ihandle, 4, 0, pa, va, size, mode) != 0)
panic("ofw_settranslation failed");
}
typedef struct _leftover {
struct _leftover *pNext;
vsize_t size;
} LEFTOVER, *PLEFTOVER;
static PLEFTOVER leftovers = NULL;
static void *
ofw_malloc(vsize_t size)
{
PLEFTOVER *ppLeftover;
PLEFTOVER pLeft;
pv_addr_t new;
vsize_t newSize, claim_size;
size = uimax(sizeof(LEFTOVER),
((size + (sizeof(LEFTOVER) - 1)) & ~(sizeof(LEFTOVER) - 1)));
for (ppLeftover = &leftovers; *ppLeftover;
ppLeftover = &((*ppLeftover)->pNext))
if ((*ppLeftover)->size >= size)
break;
if (*ppLeftover) {
new.pv_va = (vaddr_t)*ppLeftover;
if ((*ppLeftover)->size < (size + sizeof(LEFTOVER))) {
*ppLeftover = (*ppLeftover)->pNext;
} else {
pLeft = (*ppLeftover)->pNext;
newSize = (*ppLeftover)->size - size;
*ppLeftover = (PLEFTOVER)(((vaddr_t)*ppLeftover)
+ size);
(*ppLeftover)->pNext = pLeft;
(*ppLeftover)->size = newSize;
}
} else {
claim_size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
ofw_claimpages(&virt_freeptr, &new, claim_size);
if ((size + sizeof(LEFTOVER)) <= claim_size) {
pLeft = (PLEFTOVER)(new.pv_va + size);
pLeft->pNext = leftovers;
pLeft->size = claim_size - size;
leftovers = pLeft;
}
}
return (void *)(new.pv_va);
}
#if 0
static void
ofw_free(vaddr_t addr, vsize_t size)
{
PLEFTOVER pLeftover = (PLEFTOVER)addr;
pLeftover->pNext = leftovers;
pLeftover->size = size;
leftovers = pLeftover;
}
#endif
static void
ofw_claimpages(vaddr_t *free_pp, pv_addr_t *pv_p, vsize_t size)
{
vsize_t alloc_size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
vsize_t aligned_size;
vaddr_t va;
paddr_t pa;
if (alloc_size == 0)
panic("ofw_claimpages zero");
for (aligned_size = 1; aligned_size < alloc_size; aligned_size <<= 1)
;
va = (*free_pp & ~(aligned_size - 1)) - aligned_size;
if (ofw_claimvirt(va, alloc_size, 0) != va)
panic("ofw_claimpages va alloc");
pa = ofw_claimphys(0, alloc_size, aligned_size);
if (pa == -1)
panic("ofw_claimpages pa alloc");
ofw_settranslation(va, pa, alloc_size, -1);
memset((char *)va, 0, alloc_size);
*free_pp = va;
pv_p->pv_va = va;
pv_p->pv_pa = pa;
}
static void
ofw_discardmappings(vaddr_t L2pagetable, vaddr_t va, vsize_t size)
{
vsize_t alloc_size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
int npages = alloc_size / PAGE_SIZE;
if (npages == 0)
panic("ofw_discardmappings zero");
for (; npages > 0; va += PAGE_SIZE, npages--) {
if (ReadWord(L2pagetable + ((va >> 10) & 0x00000FFC)) == 0)
panic("ofw_discardmappings zero entry");
WriteWord(L2pagetable + ((va >> 10) & 0x00000FFC), 0);
}
}
static void
ofw_initallocator(void)
{
}
#if (NIGSFB_OFBUS > 0) || (NCHIPSFB_OFBUS > 0) || (NVGA_OFBUS > 0)
static void
reset_screen(void)
{
if ((console_ihandle == 0) || (console_ihandle == -1))
return;
OF_call_method("install", console_ihandle, 0, 0);
}
#endif