powerpc/mm: Abstract early MMU init in preparation for radix
[sfrench/cifs-2.6.git] / arch / powerpc / mm / hash_utils_64.c
index 7635b1c6b5dacfd04793315c31069a42a425e402..e6b53cde676ea4f1b71a24c912e9323806255953 100644 (file)
@@ -167,16 +167,22 @@ unsigned long htab_convert_pte_flags(unsigned long pteflags)
        if ((pteflags & _PAGE_EXEC) == 0)
                rflags |= HPTE_R_N;
        /*
-        * PP bits:
+        * PPP bits:
         * Linux uses slb key 0 for kernel and 1 for user.
-        * kernel areas are mapped with PP=00
-        * and there is no kernel RO (_PAGE_KERNEL_RO).
-        * User area is mapped with PP=0x2 for read/write
-        * or PP=0x3 for read-only (including writeable but clean pages).
+        * kernel RW areas are mapped with PPP=0b000
+        * User area is mapped with PPP=0b010 for read/write
+        * or PPP=0b011 for read-only (including writeable but clean pages).
         */
-       if (pteflags & _PAGE_USER) {
-               rflags |= 0x2;
-               if (!((pteflags & _PAGE_RW) && (pteflags & _PAGE_DIRTY)))
+       if (pteflags & _PAGE_PRIVILEGED) {
+               /*
+                * Kernel read only mapped with ppp bits 0b110
+                */
+               if (!(pteflags & _PAGE_WRITE))
+                       rflags |= (HPTE_R_PP0 | 0x2);
+       } else {
+               if (pteflags & _PAGE_RWX)
+                       rflags |= 0x2;
+               if (!((pteflags & _PAGE_WRITE) && (pteflags & _PAGE_DIRTY)))
                        rflags |= 0x1;
        }
        /*
@@ -186,12 +192,13 @@ unsigned long htab_convert_pte_flags(unsigned long pteflags)
        /*
         * Add in WIG bits
         */
-       if (pteflags & _PAGE_WRITETHRU)
-               rflags |= HPTE_R_W;
-       if (pteflags & _PAGE_NO_CACHE)
+
+       if ((pteflags & _PAGE_CACHE_CTL) == _PAGE_TOLERANT)
                rflags |= HPTE_R_I;
-       if (pteflags & _PAGE_GUARDED)
-               rflags |= HPTE_R_G;
+       if ((pteflags & _PAGE_CACHE_CTL ) == _PAGE_NON_IDEMPOTENT)
+               rflags |= (HPTE_R_I | HPTE_R_G);
+       if ((pteflags & _PAGE_CACHE_CTL) == _PAGE_SAO)
+               rflags |= (HPTE_R_I | HPTE_R_W);
 
        return rflags;
 }
@@ -669,6 +676,41 @@ int remove_section_mapping(unsigned long start, unsigned long end)
 }
 #endif /* CONFIG_MEMORY_HOTPLUG */
 
+static void __init hash_init_partition_table(phys_addr_t hash_table,
+                                            unsigned long pteg_count)
+{
+       unsigned long ps_field;
+       unsigned long htab_size;
+       unsigned long patb_size = 1UL << PATB_SIZE_SHIFT;
+
+       /*
+        * slb llp encoding for the page size used in VPM real mode.
+        * We can ignore that for lpid 0
+        */
+       ps_field = 0;
+       htab_size =  __ilog2(pteg_count) - 11;
+
+       BUILD_BUG_ON_MSG((PATB_SIZE_SHIFT > 24), "Partition table size too large.");
+       partition_tb = __va(memblock_alloc_base(patb_size, patb_size,
+                                               MEMBLOCK_ALLOC_ANYWHERE));
+
+       /* Initialize the Partition Table with no entries */
+       memset((void *)partition_tb, 0, patb_size);
+       partition_tb->patb0 = cpu_to_be64(ps_field | hash_table | htab_size);
+       /*
+        * FIXME!! This should be done via update_partition table
+        * For now UPRT is 0 for us.
+        */
+       partition_tb->patb1 = 0;
+       DBG("Partition table %p\n", partition_tb);
+       /*
+        * update partition table control register,
+        * 64 K size.
+        */
+       mtspr(SPRN_PTCR, __pa(partition_tb) | (PATB_SIZE_SHIFT - 12));
+
+}
+
 static void __init htab_initialize(void)
 {
        unsigned long table;
@@ -737,8 +779,11 @@ static void __init htab_initialize(void)
                /* Initialize the HPT with no entries */
                memset((void *)table, 0, htab_size_bytes);
 
-               /* Set SDR1 */
-               mtspr(SPRN_SDR1, _SDR1);
+               if (!cpu_has_feature(CPU_FTR_ARCH_300))
+                       /* Set SDR1 */
+                       mtspr(SPRN_SDR1, _SDR1);
+               else
+                       hash_init_partition_table(table, pteg_count);
        }
 
        prot = pgprot_val(PAGE_KERNEL);
@@ -823,8 +868,20 @@ static void __init htab_initialize(void)
 #undef KB
 #undef MB
 
-void __init early_init_mmu(void)
+void __init hash__early_init_mmu(void)
 {
+       /*
+        * initialize page table size
+        */
+       __pte_index_size = H_PTE_INDEX_SIZE;
+       __pmd_index_size = H_PMD_INDEX_SIZE;
+       __pud_index_size = H_PUD_INDEX_SIZE;
+       __pgd_index_size = H_PGD_INDEX_SIZE;
+       __pmd_cache_index = H_PMD_CACHE_INDEX;
+       __pte_table_size = H_PTE_TABLE_SIZE;
+       __pmd_table_size = H_PMD_TABLE_SIZE;
+       __pud_table_size = H_PUD_TABLE_SIZE;
+       __pgd_table_size = H_PGD_TABLE_SIZE;
        /* Initialize the MMU Hash table and create the linear mapping
         * of memory. Has to be done before SLB initialization as this is
         * currently where the page size encoding is obtained.
@@ -836,7 +893,7 @@ void __init early_init_mmu(void)
 }
 
 #ifdef CONFIG_SMP
-void early_init_mmu_secondary(void)
+void hash__early_init_mmu_secondary(void)
 {
        /* Initialize hash table for that CPU */
        if (!firmware_has_feature(FW_FEATURE_LPAR))
@@ -920,7 +977,7 @@ void demote_segment_4k(struct mm_struct *mm, unsigned long addr)
  * Userspace sets the subpage permissions using the subpage_prot system call.
  *
  * Result is 0: full permissions, _PAGE_RW: read-only,
- * _PAGE_USER or _PAGE_USER|_PAGE_RW: no access.
+ * _PAGE_RWX: no access.
  */
 static int subpage_protection(struct mm_struct *mm, unsigned long ea)
 {
@@ -946,8 +1003,13 @@ static int subpage_protection(struct mm_struct *mm, unsigned long ea)
        /* extract 2-bit bitfield for this 4k subpage */
        spp >>= 30 - 2 * ((ea >> 12) & 0xf);
 
-       /* turn 0,1,2,3 into combination of _PAGE_USER and _PAGE_RW */
-       spp = ((spp & 2) ? _PAGE_USER : 0) | ((spp & 1) ? _PAGE_RW : 0);
+       /*
+        * 0 -> full premission
+        * 1 -> Read only
+        * 2 -> no access.
+        * We return the flag that need to be cleared.
+        */
+       spp = ((spp & 2) ? _PAGE_RWX : 0) | ((spp & 1) ? _PAGE_WRITE : 0);
        return spp;
 }
 
@@ -1084,7 +1146,7 @@ int hash_page_mm(struct mm_struct *mm, unsigned long ea,
        /* Pre-check access permissions (will be re-checked atomically
         * in __hash_page_XX but this pre-check is a fast path
         */
-       if (access & ~pte_val(*ptep)) {
+       if (!check_pte_access(access, pte_val(*ptep))) {
                DBG_LOW(" no access !\n");
                rc = 1;
                goto bail;
@@ -1122,8 +1184,8 @@ int hash_page_mm(struct mm_struct *mm, unsigned long ea,
 #endif
        /* Do actual hashing */
 #ifdef CONFIG_PPC_64K_PAGES
-       /* If _PAGE_4K_PFN is set, make sure this is a 4k segment */
-       if ((pte_val(*ptep) & _PAGE_4K_PFN) && psize == MMU_PAGE_64K) {
+       /* If H_PAGE_4K_PFN is set, make sure this is a 4k segment */
+       if ((pte_val(*ptep) & H_PAGE_4K_PFN) && psize == MMU_PAGE_64K) {
                demote_segment_4k(mm, ea);
                psize = MMU_PAGE_4K;
        }
@@ -1131,8 +1193,7 @@ int hash_page_mm(struct mm_struct *mm, unsigned long ea,
        /* If this PTE is non-cacheable and we have restrictions on
         * using non cacheable large pages, then we switch to 4k
         */
-       if (mmu_ci_restrictions && psize == MMU_PAGE_64K &&
-           (pte_val(*ptep) & _PAGE_NO_CACHE)) {
+       if (mmu_ci_restrictions && psize == MMU_PAGE_64K && pte_ci(*ptep)) {
                if (user_region) {
                        demote_segment_4k(mm, ea);
                        psize = MMU_PAGE_4K;
@@ -1209,7 +1270,7 @@ EXPORT_SYMBOL_GPL(hash_page);
 int __hash_page(unsigned long ea, unsigned long msr, unsigned long trap,
                unsigned long dsisr)
 {
-       unsigned long access = _PAGE_PRESENT;
+       unsigned long access = _PAGE_PRESENT | _PAGE_READ;
        unsigned long flags = 0;
        struct mm_struct *mm = current->mm;
 
@@ -1220,14 +1281,18 @@ int __hash_page(unsigned long ea, unsigned long msr, unsigned long trap,
                flags |= HPTE_NOHPTE_UPDATE;
 
        if (dsisr & DSISR_ISSTORE)
-               access |= _PAGE_RW;
+               access |= _PAGE_WRITE;
        /*
-        * We need to set the _PAGE_USER bit if MSR_PR is set or if we are
-        * accessing a userspace segment (even from the kernel). We assume
-        * kernel addresses always have the high bit set.
+        * We set _PAGE_PRIVILEGED only when
+        * kernel mode access kernel space.
+        *
+        * _PAGE_PRIVILEGED is NOT set
+        * 1) when kernel mode access user space
+        * 2) user space access kernel space.
         */
+       access |= _PAGE_PRIVILEGED;
        if ((msr & MSR_PR) || (REGION_ID(ea) == USER_REGION_ID))
-               access |= _PAGE_USER;
+               access &= ~_PAGE_PRIVILEGED;
 
        if (trap == 0x400)
                access |= _PAGE_EXEC;
@@ -1282,13 +1347,13 @@ void hash_preload(struct mm_struct *mm, unsigned long ea,
 
        WARN_ON(hugepage_shift);
 #ifdef CONFIG_PPC_64K_PAGES
-       /* If either _PAGE_4K_PFN or _PAGE_NO_CACHE is set (and we are on
+       /* If either H_PAGE_4K_PFN or cache inhibited is set (and we are on
         * a 64K kernel), then we don't preload, hash_page() will take
         * care of it once we actually try to access the page.
         * That way we don't have to duplicate all of the logic for segment
         * page size demotion here
         */
-       if (pte_val(*ptep) & (_PAGE_4K_PFN | _PAGE_NO_CACHE))
+       if ((pte_val(*ptep) & H_PAGE_4K_PFN) || pte_ci(*ptep))
                goto out_exit;
 #endif /* CONFIG_PPC_64K_PAGES */
 
@@ -1570,7 +1635,7 @@ void __kernel_map_pages(struct page *page, int numpages, int enable)
 }
 #endif /* CONFIG_DEBUG_PAGEALLOC */
 
-void setup_initial_memory_limit(phys_addr_t first_memblock_base,
+void hash__setup_initial_memory_limit(phys_addr_t first_memblock_base,
                                phys_addr_t first_memblock_size)
 {
        /* We don't currently support the first MEMBLOCK not mapping 0