summaryrefslogtreecommitdiffstats
path: root/arch/sparc64/mm
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2005-10-12 12:22:46 -0700
committerDavid S. Miller <davem@davemloft.net>2005-10-12 12:22:46 -0700
commitc9c10830740df1b5e7848d6fbb68c93a73e8f7cd (patch)
treeb614058c3291ebccb996b27cee9b709956df7791 /arch/sparc64/mm
parentd8e998c58a870770905495a1d45ebf7285b5b1c5 (diff)
[SPARC64]: Fix boot failures on SunBlade-150
The sequence to move over to the Linux trap tables from the firmware ones needs to be more air tight. It turns out that to be %100 safe we do need to be able to translate OBP mappings in our TLB miss handlers early. In order not to eat up a lot of kernel image memory with static page tables, just use the translations array in the OBP TLB miss handlers. That solves the bulk of the problem. Furthermore, to make sure the OBP TLB miss path will work even before the fixed MMU globals are loaded, explicitly load %g1 to TLB_SFSR at the beginning of the i-TLB and d-TLB miss handlers. To ease the OBP TLB miss walking of the prom_trans[] array, we sort it then delete all of the non-OBP entries in there (for example, there are entries for the kernel image itself which we're not interested in at all). We also save about 32K of kernel image size with this change. Not a bad side effect :-) There are still some reasons why trampoline.S can't use the setup_trap_table() yet. The most noteworthy are: 1) OBP boots secondary processors with non-bias'd stack for some reason. This is easily fixed by using a small bootup stack in the kernel image explicitly for this purpose. 2) Doing a firmware call via the normal C call prom_set_trap_table() goes through the whole OBP enter/exit sequence that saves and restores OBP and Linux kernel state in the MMUs. This path unfortunately does a "flush %g6" while loading up the OBP locked TLB entries for the firmware call. If we setup the %g6 in the trampoline.S code properly, that is in the PAGE_OFFSET linear mapping, but we're not on the kernel trap table yet so those addresses won't translate properly. One idea is to do a by-hand firmware call like we do in the early bootup code and elsewhere here in trampoline.S But this fails as well, as aparently the secondary processors are not booted with OBP's special locked TLB entries loaded. These are necessary for the firwmare to processes TLB misses correctly up until the point where we take over the trap table. This does need to be resolved at some point. Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc64/mm')
-rw-r--r--arch/sparc64/mm/init.c187
1 files changed, 74 insertions, 113 deletions
diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c
index 0d2e967c720..1e44ee26cee 100644
--- a/arch/sparc64/mm/init.c
+++ b/arch/sparc64/mm/init.c
@@ -105,7 +105,7 @@ static void __init read_obp_memory(const char *property,
regs[i].phys_addr = base;
regs[i].reg_size = size;
}
- sort(regs, ents, sizeof(struct linux_prom64_registers),
+ sort(regs, ents, sizeof(struct linux_prom64_registers),
cmp_p64, NULL);
}
@@ -367,8 +367,11 @@ struct linux_prom_translation {
unsigned long size;
unsigned long data;
};
-static struct linux_prom_translation prom_trans[512] __initdata;
-static unsigned int prom_trans_ents __initdata;
+
+/* Exported for kernel TLB miss handling in ktlb.S */
+struct linux_prom_translation prom_trans[512] __read_mostly;
+unsigned int prom_trans_ents __read_mostly;
+unsigned int swapper_pgd_zero __read_mostly;
extern unsigned long prom_boot_page;
extern void prom_remap(unsigned long physpage, unsigned long virtpage, int mmu_ihandle);
@@ -378,122 +381,57 @@ extern void register_prom_callbacks(void);
/* Exported for SMP bootup purposes. */
unsigned long kern_locked_tte_data;
-/* Exported for kernel TLB miss handling in ktlb.S */
-unsigned long prom_pmd_phys __read_mostly;
-unsigned int swapper_pgd_zero __read_mostly;
-
-static pmd_t *prompmd __read_mostly;
-
-#define BASE_PAGE_SIZE 8192
-
/*
* Translate PROM's mapping we capture at boot time into physical address.
* The second parameter is only set from prom_callback() invocations.
*/
unsigned long prom_virt_to_phys(unsigned long promva, int *error)
{
- pmd_t *pmdp = prompmd + ((promva >> 23) & 0x7ff);
- pte_t *ptep;
- unsigned long base;
-
- if (pmd_none(*pmdp)) {
- if (error)
- *error = 1;
- return 0;
- }
- ptep = (pte_t *)__pmd_page(*pmdp) + ((promva >> 13) & 0x3ff);
- if (!pte_present(*ptep)) {
- if (error)
- *error = 1;
- return 0;
- }
- if (error) {
- *error = 0;
- return pte_val(*ptep);
- }
- base = pte_val(*ptep) & _PAGE_PADDR;
-
- return base + (promva & (BASE_PAGE_SIZE - 1));
-}
+ int i;
-/* The obp translations are saved based on 8k pagesize, since obp can
- * use a mixture of pagesizes. Misses to the LOW_OBP_ADDRESS ->
- * HI_OBP_ADDRESS range are handled in entry.S and do not use the vpte
- * scheme (also, see rant in inherit_locked_prom_mappings()).
- */
-static void __init build_obp_range(unsigned long start, unsigned long end, unsigned long data)
-{
- unsigned long vaddr;
+ for (i = 0; i < prom_trans_ents; i++) {
+ struct linux_prom_translation *p = &prom_trans[i];
- for (vaddr = start; vaddr < end; vaddr += BASE_PAGE_SIZE) {
- unsigned long val;
- pmd_t *pmd;
- pte_t *pte;
+ if (promva >= p->virt &&
+ promva < (p->virt + p->size)) {
+ unsigned long base = p->data & _PAGE_PADDR;
- pmd = prompmd + ((vaddr >> 23) & 0x7ff);
- if (pmd_none(*pmd)) {
- pte = __alloc_bootmem(BASE_PAGE_SIZE, BASE_PAGE_SIZE,
- PAGE_SIZE);
- if (!pte)
- prom_halt();
- memset(pte, 0, BASE_PAGE_SIZE);
- pmd_set(pmd, pte);
+ if (error)
+ *error = 0;
+ return base + (promva & (8192 - 1));
}
- pte = (pte_t *) __pmd_page(*pmd) + ((vaddr >> 13) & 0x3ff);
-
- val = data;
-
- /* Clear diag TTE bits. */
- if (tlb_type == spitfire)
- val &= ~0x0003fe0000000000UL;
-
- set_pte_at(&init_mm, vaddr, pte,
- __pte(val | _PAGE_MODIFIED));
-
- data += BASE_PAGE_SIZE;
}
+ if (error)
+ *error = 1;
+ return 0UL;
}
+/* The obp translations are saved based on 8k pagesize, since obp can
+ * use a mixture of pagesizes. Misses to the LOW_OBP_ADDRESS ->
+ * HI_OBP_ADDRESS range are handled in ktlb.S and do not use the vpte
+ * scheme (also, see rant in inherit_locked_prom_mappings()).
+ */
static inline int in_obp_range(unsigned long vaddr)
{
return (vaddr >= LOW_OBP_ADDRESS &&
vaddr < HI_OBP_ADDRESS);
}
-#define OBP_PMD_SIZE 2048
-static void __init build_obp_pgtable(void)
+static int cmp_ptrans(const void *a, const void *b)
{
- unsigned long i;
-
- prompmd = __alloc_bootmem(OBP_PMD_SIZE, OBP_PMD_SIZE, PAGE_SIZE);
- if (!prompmd)
- prom_halt();
-
- memset(prompmd, 0, OBP_PMD_SIZE);
-
- prom_pmd_phys = __pa(prompmd);
-
- for (i = 0; i < prom_trans_ents; i++) {
- unsigned long start, end;
-
- if (!in_obp_range(prom_trans[i].virt))
- continue;
+ const struct linux_prom_translation *x = a, *y = b;
- start = prom_trans[i].virt;
- end = start + prom_trans[i].size;
- if (end > HI_OBP_ADDRESS)
- end = HI_OBP_ADDRESS;
-
- build_obp_range(start, end, prom_trans[i].data);
- }
+ if (x->virt > y->virt)
+ return 1;
+ if (x->virt < y->virt)
+ return -1;
+ return 0;
}
-/* Read OBP translations property into 'prom_trans[]'.
- * Return the number of entries.
- */
+/* Read OBP translations property into 'prom_trans[]'. */
static void __init read_obp_translations(void)
{
- int n, node;
+ int n, node, ents, first, last, i;
node = prom_finddevice("/virtual-memory");
n = prom_getproplen(node, "translations");
@@ -515,7 +453,41 @@ static void __init read_obp_translations(void)
n = n / sizeof(struct linux_prom_translation);
- prom_trans_ents = n;
+ ents = n;
+
+ sort(prom_trans, ents, sizeof(struct linux_prom_translation),
+ cmp_ptrans, NULL);
+
+ /* Now kick out all the non-OBP entries. */
+ for (i = 0; i < ents; i++) {
+ if (in_obp_range(prom_trans[i].virt))
+ break;
+ }
+ first = i;
+ for (; i < ents; i++) {
+ if (!in_obp_range(prom_trans[i].virt))
+ break;
+ }
+ last = i;
+
+ for (i = 0; i < (last - first); i++) {
+ struct linux_prom_translation *src = &prom_trans[i + first];
+ struct linux_prom_translation *dest = &prom_trans[i];
+
+ *dest = *src;
+ }
+ for (; i < ents; i++) {
+ struct linux_prom_translation *dest = &prom_trans[i];
+ dest->virt = dest->size = dest->data = 0x0UL;
+ }
+
+ prom_trans_ents = last - first;
+
+ if (tlb_type == spitfire) {
+ /* Clear diag TTE bits. */
+ for (i = 0; i < prom_trans_ents; i++)
+ prom_trans[i].data &= ~0x0003fe0000000000UL;
+ }
}
static void __init remap_kernel(void)
@@ -553,21 +525,18 @@ static void __init remap_kernel(void)
}
-static void __init inherit_prom_mappings_pre(void)
+static void __init inherit_prom_mappings(void)
{
read_obp_translations();
/* Now fixup OBP's idea about where we really are mapped. */
prom_printf("Remapping the kernel... ");
remap_kernel();
-
prom_printf("done.\n");
-}
-static void __init inherit_prom_mappings_post(void)
-{
- build_obp_pgtable();
+ prom_printf("Registering callbacks... ");
register_prom_callbacks();
+ prom_printf("done.\n");
}
/* The OBP specifications for sun4u mark 0xfffffffc00000000 and
@@ -1519,7 +1488,7 @@ void __init paging_init(void)
swapper_pgd_zero = pgd_val(swapper_pg_dir[0]);
- inherit_prom_mappings_pre();
+ inherit_prom_mappings();
/* Ok, we can use our TLB miss and window trap handlers safely.
* We need to do a quick peek here to see if we are on StarFire
@@ -1530,23 +1499,15 @@ void __init paging_init(void)
extern void setup_tba(int);
setup_tba(this_is_starfire);
}
- __flush_tlb_all();
- /* Everything from this point forward, until we are done with
- * inherit_prom_mappings_post(), must complete successfully
- * without calling into the firmware. The firwmare page tables
- * have not been built, but we are running on the Linux kernel's
- * trap table.
- */
+ inherit_locked_prom_mappings(1);
+
+ __flush_tlb_all();
/* Setup bootmem... */
pages_avail = 0;
last_valid_pfn = end_pfn = bootmem_init(&pages_avail);
- inherit_prom_mappings_post();
-
- inherit_locked_prom_mappings(1);
-
#ifdef CONFIG_DEBUG_PAGEALLOC
kernel_physical_mapping_init();
#endif