/* Memory utilities Copyright (C) 1995, 1996 David S. Miller 1996 Jakub Jelinek 1996 Andrew Tridgell This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "silo.h" struct linux_prom_registers prom_reg_memlist[64]; struct linux_mlist_v0 prom_phys_avail[64]; /* Internal Prom library routine to sort a linux_mlist_v0 memory * list. Used below in initialization. */ static void prom_sortmemlist (struct linux_mlist_v0 *thislist) { int swapi = 0; int i, mitr, tmpsize; char *tmpaddr; char *lowest; for (i = 0; thislist[i].theres_more != 0; i++) { lowest = thislist[i].start_adr; for (mitr = i + 1; thislist[mitr - 1].theres_more != 0; mitr++) if (thislist[mitr].start_adr < lowest) { lowest = thislist[mitr].start_adr; swapi = mitr; } if (lowest == thislist[i].start_adr) continue; tmpaddr = thislist[swapi].start_adr; tmpsize = thislist[swapi].num_bytes; for (mitr = swapi; mitr > i; mitr--) { thislist[mitr].start_adr = thislist[mitr - 1].start_adr; thislist[mitr].num_bytes = thislist[mitr - 1].num_bytes; } thislist[i].start_adr = tmpaddr; thislist[i].num_bytes = tmpsize; } } /* Initialize the memory lists based upon the prom version. */ struct linux_mlist_v0 *prom_meminit (void) { int node = 0; unsigned int iter, num_regs; struct linux_mlist_v0 *mptr; /* ptr for traversal */ static meminited = 0; if (meminited) return prom_phys_avail; meminited = 1; switch (romvec->pv_romvers) { case 0: case 1: /* Nice, kind of easier to do in this case. */ /* First, the total physical descriptors. */ /* Last, the available physical descriptors. */ for (mptr = (*(romvec->pv_v0mem.v0_available)), iter = 0; mptr; mptr = mptr->theres_more, iter++) { prom_phys_avail[iter].start_adr = mptr->start_adr; prom_phys_avail[iter].num_bytes = mptr->num_bytes; prom_phys_avail[iter].theres_more = &prom_phys_avail[iter + 1]; } prom_phys_avail[iter - 1].theres_more = 0; prom_sortmemlist (prom_phys_avail); break; case 2: case 3: /* Grrr, have to traverse the prom device tree ;( */ node = prom_getchild (prom_root_node); node = prom_searchsiblings (node, "memory"); num_regs = prom_getproperty (node, "available", (char *) prom_reg_memlist, sizeof (prom_reg_memlist)); num_regs = (num_regs / sizeof (struct linux_prom_registers)); for (iter = 0; iter < num_regs; iter++) { prom_phys_avail[iter].start_adr = prom_reg_memlist[iter].phys_addr; prom_phys_avail[iter].num_bytes = (unsigned long) prom_reg_memlist[iter].reg_size; prom_phys_avail[iter].theres_more = &prom_phys_avail[iter + 1]; } prom_phys_avail[iter - 1].theres_more = 0; prom_sortmemlist (prom_phys_avail); break; } return prom_phys_avail; } static int sun4c_hwflushes; static int sun4c_linesize; static void sun4c_init (void) { static int inited = 0; int propval; if (!inited) { inited = 1; propval = prom_getintdefault (prom_root_node, "vac_hwflush", -1); sun4c_hwflushes = (propval == -1) ? prom_getintdefault (prom_root_node, "vac-hwflush", 0) : propval; sun4c_linesize = prom_getintdefault (prom_root_node, "vac-linesize", 16); } } void sun4c_map (unsigned long virtual, unsigned long page) { unsigned long virt = virtual; sun4c_init (); if (sun4c_hwflushes) { __asm__ __volatile__ ("\n\tsta %%g0, [%0] 0x06\n\t" : : "r" (virt)); } else { unsigned long end = virt + 4096; for (; virt < end; virt += sun4c_linesize) __asm__ __volatile__ ("\n\tsta %%g0, [%0] 0x0d\n\t" : : "r" (virt)); } virt = virtual; __asm__ __volatile__ ("\n\tsta %1, [%0] 0x04\n\t" : : "r" (virt), "r" (page)); } int sun4c_mapio (unsigned long phys, unsigned long virtual, int rdonly) { unsigned long page = ((phys >> 12) & 0xffff) | 0x94000000; if (!rdonly) page |= 0x40000000; sun4c_map (virtual & ~4095, page); return 0; } void sun4c_unmapio (unsigned long virtual) { sun4c_map (virtual & ~4095, 0); } void ensure_sun4c_mapping (char *beg, int len) { /* Not written yet */ } inline unsigned long sun4m_get_lev1 (void) { unsigned long ret; __asm__ ("\n\t" "set 0x100, %0\n\t" "lda [%0] 4, %0\n\t" "sll %0, 4, %0\n\t" "lda [%0] 32, %0\n\t" "srl %0, 4, %0\n\t" "sll %0, 8, %0\n\t" : "=r" (ret)); return ret; } inline unsigned long sun4m_get_direct (unsigned long l) { unsigned long ret; __asm__ ("\n\t" "lda [%1] 32, %0\n\t" : "=r" (ret) : "r" (l)); return ret; } #define SUN4M_MAPIT \ if ((char *)(*romvec->pv_v2devops.v2_dumb_mmap)(start, 0, (unsigned)start, cur - start) == start) \ start = (char *)-1; \ else \ break; void ensure_sun4m_mapping (char *beg, int len) { char *end = beg + len, *cur = beg; unsigned long lev1 = sun4m_get_lev1 (), lev2, lev3; char *start = (char *)-1; while (cur < end) { if (!((unsigned)cur & 0xffffff) || cur == beg) { lev2 = sun4m_get_direct (lev1 + (((unsigned)cur >> 24) << 2)); if (!(lev2 & 0x3)) { if (start == (char *)-1) start = cur; cur = (char *)((unsigned)cur & 0xff000000) + 0x1000000; continue; } else { if (start != (char *)-1) { SUN4M_MAPIT } lev2 &= ~0xf; lev2 <<= 4; } } if (!((unsigned)cur & 0x3ffff) || cur == beg) { lev3 = sun4m_get_direct (lev2 + ((((unsigned)cur >> 18) & 0x3f) << 2)); if (!(lev3 & 0x3)) { if (start == (char *)-1) start = cur; cur = (char *)((unsigned)cur & 0xfffc0000) + 0x40000; continue; } else { if (start != (char *)-1) { SUN4M_MAPIT } lev3 &= ~0xf; lev3 <<= 4; } } if (sun4m_get_direct (lev3 + ((((unsigned)cur >> 12) & 0x3f) << 2)) & 0x3) { if (start != (char *)-1) { SUN4M_MAPIT } } else { if (start == (char *)-1) { start = cur; } } cur += 4096; } if (start != (char *)-1) { if ((char *)(*romvec->pv_v2devops.v2_dumb_mmap)(start, 0, (unsigned)start, end - start) != start) { printf ("Fatal error: Cannot create identical memory mapping\n"); (*romvec->pv_halt)(); } } } char *memory_find (int len) { register struct linux_mlist_v0 *mlist; char *beg = 0, *start; int l = 0, num; prom_meminit (); mlist = prom_phys_avail; for (;;) { if (beg && mlist->start_adr != beg + l) beg = 0; start = mlist->start_adr; num = mlist->num_bytes; if (start <= (char *)0x300000) { num += start - (char *)0x300000; start = (char *)0x300000; } if (num > 0) { if (num + (beg ? l : 0) >= len) { if (!beg) beg = start; switch (architecture) { case sun4c: ensure_sun4c_mapping (beg, len); break; case sun4m: ensure_sun4m_mapping (beg, len); break; } return beg; } if (beg) l += num; else { beg = start; l = num; } } if (!mlist->theres_more) return 0; mlist = mlist->theres_more; } }