411 lines
9.8 KiB
C
411 lines
9.8 KiB
C
/* lib.c
|
|
* tag: simple function library
|
|
*
|
|
* Copyright (C) 2003 Stefan Reinauer
|
|
*
|
|
* See the file "COPYING" for further information about
|
|
* the copyright and warranty status of this work.
|
|
*/
|
|
|
|
#include "libc/vsprintf.h"
|
|
#include "libopenbios/bindings.h"
|
|
#include "arch/sparc32/ofmem_sparc32.h"
|
|
#include "asm/asi.h"
|
|
#include "arch/sparc32/pgtsrmmu.h"
|
|
#include "openprom.h"
|
|
#include "libopenbios/sys_info.h"
|
|
#include "boot.h"
|
|
#include "romvec.h"
|
|
|
|
#define NCTX_SWIFT 0x100
|
|
#define LOWMEMSZ 32 * 1024 * 1024
|
|
|
|
#ifdef CONFIG_DEBUG_MEM
|
|
#define DPRINTF(fmt, args...) \
|
|
do { printk(fmt , ##args); } while (0)
|
|
#else
|
|
#define DPRINTF(fmt, args...)
|
|
#endif
|
|
|
|
/* Format a string and print it on the screen, just like the libc
|
|
* function printf.
|
|
*/
|
|
int printk( const char *fmt, ... )
|
|
{
|
|
char *p, buf[512];
|
|
va_list args;
|
|
int i;
|
|
|
|
va_start(args, fmt);
|
|
i = vsnprintf(buf, sizeof(buf), fmt, args);
|
|
va_end(args);
|
|
|
|
for( p=buf; *p; p++ )
|
|
putchar(*p);
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* Allocatable memory chunk.
|
|
*/
|
|
struct mem {
|
|
char *start, *uplim;
|
|
char *curp;
|
|
};
|
|
|
|
struct mem cdvmem; /* Current device virtual memory space */
|
|
|
|
unsigned int va_shift;
|
|
unsigned long *l1;
|
|
static unsigned long *context_table;
|
|
|
|
struct linux_mlist_v0 *ptphys;
|
|
struct linux_mlist_v0 *ptmap;
|
|
struct linux_mlist_v0 *ptavail;
|
|
|
|
/* Private functions for mapping between physical/virtual addresses */
|
|
phys_addr_t
|
|
va2pa(unsigned long va)
|
|
{
|
|
if ((va >= (unsigned long)&_start) &&
|
|
(va < (unsigned long)&_end))
|
|
return va - va_shift;
|
|
else
|
|
return va;
|
|
}
|
|
|
|
unsigned long
|
|
pa2va(phys_addr_t pa)
|
|
{
|
|
if ((pa + va_shift >= (unsigned long)&_start) &&
|
|
(pa + va_shift < (unsigned long)&_end))
|
|
return pa + va_shift;
|
|
else
|
|
return pa;
|
|
}
|
|
|
|
void *
|
|
malloc(int size)
|
|
{
|
|
return ofmem_malloc(size);
|
|
}
|
|
|
|
void *
|
|
realloc( void *ptr, size_t size )
|
|
{
|
|
return ofmem_realloc(ptr, size);
|
|
}
|
|
|
|
void
|
|
free(void *ptr)
|
|
{
|
|
ofmem_free(ptr);
|
|
}
|
|
|
|
/*
|
|
* Allocate memory. This is reusable.
|
|
*/
|
|
void
|
|
mem_init(struct mem *t, char *begin, char *limit)
|
|
{
|
|
t->start = begin;
|
|
t->uplim = limit;
|
|
t->curp = begin;
|
|
}
|
|
|
|
void *
|
|
mem_alloc(struct mem *t, int size, int align)
|
|
{
|
|
char *p;
|
|
unsigned long pa;
|
|
|
|
// The alignment restrictions refer to physical, not virtual
|
|
// addresses
|
|
pa = va2pa((unsigned long)t->curp) + (align - 1);
|
|
pa &= ~(align - 1);
|
|
p = (char *)pa2va(pa);
|
|
|
|
if ((unsigned long)p >= (unsigned long)t->uplim ||
|
|
(unsigned long)p + size > (unsigned long)t->uplim)
|
|
return NULL;
|
|
t->curp = p + size;
|
|
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
* D5.3 pgmap@ ( va -- pte )
|
|
*/
|
|
static void
|
|
pgmap_fetch(void)
|
|
{
|
|
uint32_t pte;
|
|
unsigned long va, pa;
|
|
|
|
va = POP();
|
|
|
|
pa = find_pte(va, 0);
|
|
if (pa == 1 || pa == 2)
|
|
goto error;
|
|
pte = *(uint32_t *)pa;
|
|
DPRINTF("pgmap@: va 0x%lx pa 0x%lx pte 0x%x\n", va, pa, pte);
|
|
|
|
PUSH(pte);
|
|
return;
|
|
error:
|
|
PUSH(0);
|
|
}
|
|
|
|
/*
|
|
* D5.3 pgmap! ( pte va -- )
|
|
*/
|
|
static void
|
|
pgmap_store(void)
|
|
{
|
|
uint32_t pte;
|
|
unsigned long va, pa;
|
|
|
|
va = POP();
|
|
pte = POP();
|
|
|
|
pa = find_pte(va, 1);
|
|
*(uint32_t *)pa = pte;
|
|
DPRINTF("pgmap!: va 0x%lx pa 0x%lx pte 0x%x\n", va, pa, pte);
|
|
}
|
|
|
|
/*
|
|
* D5.3 map-pages ( pa space va size -- )
|
|
*/
|
|
static void
|
|
ob_map_pages(void)
|
|
{
|
|
unsigned long va;
|
|
int size;
|
|
uint64_t pa;
|
|
|
|
size = POP();
|
|
va = POP();
|
|
pa = POP();
|
|
pa <<= 32;
|
|
pa |= POP() & 0xffffffff;
|
|
|
|
ofmem_arch_map_pages(pa, va, size, ofmem_arch_default_translation_mode(pa));
|
|
}
|
|
|
|
char *obp_dumb_mmap(char *va, int which_io, unsigned int pa,
|
|
unsigned int size)
|
|
{
|
|
uint64_t mpa = ((uint64_t)which_io << 32) | (uint64_t)pa;
|
|
ucell virt;
|
|
|
|
DPRINTF("obp_dumb_mmap: virta 0x%x, phys 0x%x, size %d\n", (unsigned int)va, pa, size);
|
|
|
|
/* Claim virtual memory */
|
|
virt = ofmem_claim_virt(pointer2cell(va), size, 0);
|
|
|
|
/* Map memory */
|
|
ofmem_map(mpa, virt, size, ofmem_arch_default_translation_mode(mpa));
|
|
|
|
return cell2pointer(virt);
|
|
}
|
|
|
|
void obp_dumb_munmap(char *va, unsigned int size)
|
|
{
|
|
DPRINTF("obp_dumb_munmap: virta 0x%x, sz %d\n", (unsigned int)va, size);
|
|
|
|
ofmem_unmap(pointer2cell(va), size);
|
|
ofmem_release_virt(pointer2cell(va), size);
|
|
}
|
|
|
|
char *obp_memalloc(char *va, unsigned int size, unsigned int align)
|
|
{
|
|
phys_addr_t phys;
|
|
ucell virt;
|
|
|
|
DPRINTF("obp_memalloc: virta 0x%x, sz %d, align %d\n", (unsigned int)va, size, align);
|
|
|
|
/* Claim physical memory */
|
|
phys = ofmem_claim_phys(-1, size, align);
|
|
|
|
/* Claim virtual memory */
|
|
virt = ofmem_claim_virt(pointer2cell(va), size, 0);
|
|
|
|
/* Map the memory */
|
|
ofmem_map(phys, virt, size, ofmem_arch_default_translation_mode(phys));
|
|
|
|
return cell2pointer(virt);
|
|
}
|
|
|
|
char *obp_dumb_memalloc(char *va, unsigned int size)
|
|
{
|
|
unsigned long align = size;
|
|
phys_addr_t phys;
|
|
ucell virt;
|
|
|
|
DPRINTF("obp_dumb_memalloc: virta 0x%x, sz %d\n", (unsigned int)va, size);
|
|
|
|
/* Solaris seems to assume that the returned value is physically aligned to size.
|
|
e.g. it is used for setting up page tables. */
|
|
|
|
/* Claim physical memory */
|
|
phys = ofmem_claim_phys(-1, size, align);
|
|
|
|
/* Claim virtual memory - if va == NULL then we choose va address */
|
|
if (va == NULL) {
|
|
virt = ofmem_claim_virt((ucell)-1, size, align);
|
|
} else {
|
|
virt = ofmem_claim_virt(pointer2cell(va), size, 0);
|
|
}
|
|
|
|
/* Map the memory */
|
|
ofmem_map(phys, virt, size, ofmem_arch_default_translation_mode(phys));
|
|
|
|
return cell2pointer(virt);
|
|
}
|
|
|
|
void obp_dumb_memfree(char *va, unsigned size)
|
|
{
|
|
phys_addr_t phys;
|
|
ucell cellmode;
|
|
|
|
DPRINTF("obp_dumb_memfree: virta 0x%x, sz %d\n", (unsigned int)va, size);
|
|
|
|
phys = ofmem_translate(pointer2cell(va), &cellmode);
|
|
|
|
ofmem_unmap(pointer2cell(va), size);
|
|
ofmem_release_virt(pointer2cell(va), size);
|
|
ofmem_release_phys(phys, size);
|
|
}
|
|
|
|
/* Data fault handling routines */
|
|
|
|
extern unsigned int ignore_dfault;
|
|
|
|
/* ( -- reg ) */
|
|
static void srmmu_get_sfsr(void)
|
|
{
|
|
PUSH(srmmu_get_fstatus());
|
|
}
|
|
|
|
/* ( -- addr ) */
|
|
static void ignore_dfault_addr(void)
|
|
{
|
|
PUSH(pointer2cell(&ignore_dfault));
|
|
}
|
|
|
|
void
|
|
ob_init_mmu(uint32_t simm_size)
|
|
{
|
|
ucell *memreg;
|
|
ucell *virtreg;
|
|
phys_addr_t virtregsize;
|
|
ofmem_t *ofmem = ofmem_arch_get_private();
|
|
int i, c;
|
|
|
|
/* Find the phandles for the /memory and /virtual-memory nodes */
|
|
push_str("/memory");
|
|
fword("find-package");
|
|
POP();
|
|
s_phandle_memory = POP();
|
|
|
|
push_str("/virtual-memory");
|
|
fword("find-package");
|
|
POP();
|
|
s_phandle_mmu = POP();
|
|
|
|
ofmem_register(s_phandle_memory, s_phandle_mmu);
|
|
|
|
/* Setup /memory:reg (totphys) property */
|
|
c = ofmem->ramsize / simm_size;
|
|
memreg = malloc(3 * c * sizeof(ucell));
|
|
for (i = 0; i < c; i++) {
|
|
ofmem_arch_encode_physaddr(&memreg[i * 3], simm_size * i); /* physical base */
|
|
memreg[i * 3 + 2] = simm_size; /* size */
|
|
}
|
|
|
|
push_str("/memory");
|
|
fword("find-device");
|
|
PUSH(pointer2cell(memreg));
|
|
PUSH(3 * c * sizeof(ucell));
|
|
push_str("reg");
|
|
PUSH_ph(s_phandle_memory);
|
|
fword("encode-property");
|
|
|
|
/* Setup /virtual-memory:reg property */
|
|
virtregsize = ((phys_addr_t)((ucell)-1) + 1) / 2;
|
|
|
|
virtreg = malloc(6 * sizeof(ucell));
|
|
ofmem_arch_encode_physaddr(virtreg, 0);
|
|
virtreg[2] = virtregsize;
|
|
ofmem_arch_encode_physaddr(&virtreg[3], virtregsize);
|
|
virtreg[5] = virtregsize;
|
|
|
|
push_str("/virtual-memory");
|
|
fword("find-device");
|
|
PUSH(pointer2cell(virtreg));
|
|
PUSH(6 * sizeof(ucell));
|
|
push_str("reg");
|
|
PUSH_ph(s_phandle_mmu);
|
|
fword("encode-property");
|
|
|
|
PUSH(0);
|
|
fword("active-package!");
|
|
bind_func("pgmap@", pgmap_fetch);
|
|
bind_func("pgmap!", pgmap_store);
|
|
bind_func("map-pages", ob_map_pages);
|
|
|
|
/* Install data fault handler words for cpeek etc. */
|
|
PUSH_xt(bind_noname_func(srmmu_get_sfsr));
|
|
feval("to sfsr@");
|
|
PUSH_xt(bind_noname_func(ignore_dfault_addr));
|
|
feval("to ignore-dfault");
|
|
}
|
|
|
|
/*
|
|
* Switch page tables.
|
|
*/
|
|
void
|
|
init_mmu_swift(void)
|
|
{
|
|
unsigned int addr, i;
|
|
unsigned long pa, va;
|
|
int size;
|
|
|
|
ofmem_posix_memalign((void *)&context_table, NCTX_SWIFT * sizeof(int),
|
|
NCTX_SWIFT * sizeof(int));
|
|
ofmem_posix_memalign((void *)&l1, 256 * sizeof(int), 256 * sizeof(int));
|
|
|
|
context_table[0] = (((unsigned long)va2pa((unsigned long)l1)) >> 4) |
|
|
SRMMU_ET_PTD;
|
|
|
|
for (i = 1; i < NCTX_SWIFT; i++) {
|
|
context_table[i] = SRMMU_ET_INVALID;
|
|
}
|
|
for (i = 0; i < 256; i++) {
|
|
l1[i] = SRMMU_ET_INVALID;
|
|
}
|
|
|
|
// text, rodata, data, and bss mapped to end of RAM
|
|
va = (unsigned long)&_start;
|
|
size = (unsigned long)&_end - (unsigned long)&_start;
|
|
pa = va2pa(va);
|
|
ofmem_arch_map_pages(pa, va, size, ofmem_arch_default_translation_mode(pa));
|
|
ofmem_map_page_range(pa, va, size, ofmem_arch_default_translation_mode(pa));
|
|
|
|
// 1:1 mapping for RAM (don't map page 0 to allow catching of NULL dereferences)
|
|
ofmem_arch_map_pages(PAGE_SIZE, PAGE_SIZE, LOWMEMSZ - PAGE_SIZE, ofmem_arch_default_translation_mode(0));
|
|
ofmem_map_page_range(PAGE_SIZE, PAGE_SIZE, LOWMEMSZ - PAGE_SIZE, ofmem_arch_default_translation_mode(0));
|
|
|
|
/*
|
|
* Flush cache
|
|
*/
|
|
for (addr = 0; addr < 0x2000; addr += 0x10) {
|
|
__asm__ __volatile__ ("sta %%g0, [%0] %1\n\t" : :
|
|
"r" (addr), "i" (ASI_M_DATAC_TAG));
|
|
__asm__ __volatile__ ("sta %%g0, [%0] %1\n\t" : :
|
|
"r" (addr<<1), "i" (ASI_M_TXTC_TAG));
|
|
}
|
|
srmmu_set_context(0);
|
|
srmmu_set_ctable_ptr(va2pa((unsigned long)context_table));
|
|
srmmu_flush_whole_tlb();
|
|
}
|