/* * Initial implementation: * Copyright (c) 2001 Robert Drehmel * All rights reserved. * * As long as the above copyright statement and this notice remain * unchanged, you can do what ever you want with this file. * * $FreeBSD$ */ /* * FreeBSD/sparc64 kernel loader - machine dependent part * * - implements copyin and readin functions that map kernel * pages on demand. The machine independent code does not * know the size of the kernel early enough to pre-enter * TTEs and install just one 4MB mapping seemed to limiting * to me. */ #include #include #include #include #include #include #include #include #include "bootstrap.h" #include "libofw.h" enum { HEAPVA = 0x800000, HEAPSZ = 0x1000000, LOADSZ = 0x1000000 /* for kernel and modules */ }; struct memory_slice { vm_offset_t pstart; vm_offset_t size; }; extern int ofw_gate(void *); extern void itlb_enter(int, vm_offset_t, vm_offset_t, unsigned long); extern void dtlb_enter(int, vm_offset_t, vm_offset_t, unsigned long); extern vm_offset_t itlb_va_to_pa(vm_offset_t); extern vm_offset_t dtlb_va_to_pa(vm_offset_t); extern void jmpkern(vm_offset_t, struct bootinfo *); static int elf_exec(struct preloaded_file *); static int sparc64_autoload(void); static int mmu_mapin(vm_offset_t, vm_size_t); char __progname[] = "FreeBSD/sparc64 loader"; vm_offset_t kernelpa; /* Begin of kernel and mod memory. */ vm_offset_t curkpg; /* (PA) used for on-demand map-in. */ vm_offset_t curkva = 0; vm_offset_t heapva; int tlbslot = 60; /* Insert first entry at this TLB slot. */ phandle_t pmemh; /* OFW memory handle */ struct memory_slice memslices[18]; struct ofw_devdesc bootdev; /* * Machine dependent structures that the machine independent * loader part uses. */ struct devsw *devsw[] = { &ofwdisk, 0 }; struct arch_switch archsw; struct file_format sparc64_elf = { elf_loadfile, elf_exec }; struct file_format *file_formats[] = { &sparc64_elf, 0 }; struct fs_ops *file_system[] = { &ufs_fsops, 0 }; extern struct console ofwconsole; struct console *consoles[] = { &ofwconsole, 0 }; /* * archsw functions */ static int sparc64_autoload(void) { printf("nothing to autoload yet.\n"); return 0; } static ssize_t sparc64_readin(const int fd, vm_offset_t va, const size_t len) { mmu_mapin(va, len); return read(fd, (void *)va, len); } static ssize_t sparc64_copyin(const void *src, vm_offset_t dest, size_t len) { mmu_mapin(dest, len); memcpy((void *)dest, src, len); return len; } /* * other MD functions */ static int elf_exec(struct preloaded_file *fp) { struct file_metadata *fmp; struct bootinfo bi, *bip; Elf_Ehdr *Ehdr; vm_offset_t entry; if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == 0) { return EFTYPE; } Ehdr = (Elf_Ehdr *)&fmp->md_data; entry = Ehdr->e_entry; /* align the bootinfo structure on an eight byte boundary */ bip = (struct bootinfo *)((curkva + 8) & 0x7); bi.bi_version = BOOTINFO_VERSION; bi.bi_kpa = kernelpa; bi.bi_end = (vm_offset_t)(bip + 1); bi.bi_metadata = 0; sparc64_copyin(&bi, (vm_offset_t)bip, sizeof(struct bootinfo)); printf("jumping to kernel entry at 0x%lx.\n", entry); #if 0 pmap_print_tlb('i'); pmap_print_tlb('d'); #endif jmpkern(entry, bip); return 1; } static int mmu_mapin(vm_offset_t va, vm_size_t len) { printf("mmu_mapin(): access to 0x%lx-0x%lx requested\n", va, va + len); if (va + len > curkva) curkva = va + len; len += va & 0x3fffff; va &= ~0x3fffff; while (len) { if (dtlb_va_to_pa(va) == (vm_offset_t)-1 || itlb_va_to_pa(va) == (vm_offset_t)-1) { printf("mmu_mapin(): map pa 0x%lx as va 0x%lx.\n", curkpg, va); dtlb_enter(tlbslot, curkpg, va, TD_V | TD_4M | TD_L | TD_CP | TD_CV | TD_P | TD_W); itlb_enter(tlbslot, curkpg, va, TD_V | TD_4M | TD_L | TD_CP | TD_CV | TD_P | TD_W); tlbslot--; curkpg += 0x400000; } len -= len > 0x400000 ? 0x400000 : len; va += 0x400000; } return 0; } static vm_offset_t init_heap(void) { if ((pmemh = OF_finddevice("/memory")) == (phandle_t)-1) OF_exit(); if (OF_getprop(pmemh, "reg", memslices, sizeof(memslices)) <= 0) OF_exit(); /* Reserve 16 MB continuous for kernel and modules. */ kernelpa = (vm_offset_t)OF_alloc_phys(LOADSZ, 0x400000); curkpg = kernelpa; /* There is no need for continuous physical heap memory. */ heapva = (vm_offset_t)OF_claim((void *)HEAPVA, HEAPSZ, 32); return heapva; } int main(int (*openfirm)(void *)) { char bootpath[64]; struct devsw **dp; phandle_t chosenh; /* * Tell the OpenFirmware functions where they find the ofw gate. */ OF_init(&ofw_gate); archsw.arch_getdev = ofw_getdev; archsw.arch_copyin = sparc64_copyin; archsw.arch_copyout = ofw_copyout; archsw.arch_readin = sparc64_readin; archsw.arch_autoload = sparc64_autoload; init_heap(); setheap((void *)heapva, (void *)(heapva + HEAPSZ)); /* * Probe for a console. */ cons_probe(); bcache_init(32, 512); /* * Initialize devices. */ for (dp = devsw; *dp != 0; dp++) { if ((*dp)->dv_init != 0) (*dp)->dv_init(); } /* * Set up the current device. */ chosenh = OF_finddevice("/chosen"); OF_getprop(chosenh, "bootpath", bootpath, sizeof(bootpath)); bootdev.d_type = ofw_devicetype(bootpath); switch (bootdev.d_type) { case DEVT_DISK: bootdev.d_dev = &ofwdisk; strncpy(bootdev.d_kind.ofwdisk.path, bootpath, 64); ofw_parseofwdev(&bootdev, bootpath); break; case DEVT_NET: //bootdev.d_dev = &netdev; strncpy(bootdev.d_kind.netif.path, bootpath, 64); bootdev.d_kind.netif.unit = 0; break; } env_setenv("currdev", EV_VOLATILE, ofw_fmtdev(&bootdev), ofw_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, ofw_fmtdev(&bootdev), env_noset, env_nounset); printf("%s\n", __progname); printf("bootpath=\"%s\"\n", bootpath); printf("loaddev=%s\n", getenv("loaddev")); printf("kernelpa=0x%lx\n", curkpg); /* Give control to the machine independent loader code. */ interact(); return 1; } typedef u_int64_t tte_t; const char *page_sizes[] = { " 8k", " 64k", "512k", " 4m" }; static void pmap_print_tte(tte_t tag, tte_t tte) { printf("%s %s ", page_sizes[(tte & TD_SIZE_MASK) >> TD_SIZE_SHIFT], tag & TD_G ? "G" : " "); printf(tte & TD_W ? "W " : " "); printf(tte & TD_P ? "\e[33mP\e[0m " : " "); printf(tte & TD_E ? "E " : " "); printf(tte & TD_CV ? "CV " : " "); printf(tte & TD_CP ? "CP " : " "); printf(tte & TD_L ? "\e[32mL\e[0m " : " "); printf(tte & TD_IE ? "IE " : " "); printf(tte & TD_NFO ? "NFO " : " "); printf("tag=0x%lx pa=0x%lx va=0x%lx ctx=%ld\n", tag, TD_PA(tte), TT_VA(tag), TT_CTX(tag)); } void pmap_print_tlb(char which) { int i; tte_t tte, tag; for (i = 0; i < 64*8; i += 8) { if (which == 'i') { __asm__ __volatile__("ldxa [%1] %2, %0\n" : "=r" (tag) : "r" (i), "i" (ASI_ITLB_TAG_READ_REG)); __asm__ __volatile__("ldxa [%1] %2, %0\n" : "=r" (tte) : "r" (i), "i" (ASI_ITLB_DATA_ACCESS_REG)); } else { __asm__ __volatile__("ldxa [%1] %2, %0\n" : "=r" (tag) : "r" (i), "i" (ASI_DTLB_TAG_READ_REG)); __asm__ __volatile__("ldxa [%1] %2, %0\n" : "=r" (tte) : "r" (i), "i" (ASI_DTLB_DATA_ACCESS_REG)); } if (!(tte & TD_V)) continue; printf("%cTLB-%2u: ", which, i>>3); pmap_print_tte(tag, tte); } }