Message ID | 1433593888-7142-1-git-send-email-stefan@agner.ch (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Sat, Jun 06, 2015 at 02:31:28PM +0200, Stefan Agner wrote: > Add early fixmap support, initially to support permanent, fixed > mapping support for early console. A temporary, early pte is > created which is migrated to a permanent mapping in paging_init. > This is also needed since the attributes may change as the memory > types are initialized. The 3MiB range of fixmap spans two pte > tables, but currently only one pte is created for early fixmap > support. > > Re-add FIX_KMAP_BEGIN to the index calculation in highmem.c since > the index for kmap does not start at zero anymore. This reverts > 4221e2e6b316 ("ARM: 8031/1: fixmap: remove FIX_KMAP_BEGIN and > FIX_KMAP_END") to some extent. > > Cc: Mark Salter <msalter@redhat.com> > Cc: Russell King <linux@arm.linux.org.uk> > Cc: Kees Cook <keescook@chromium.org> > Cc: Laura Abbott <lauraa@codeaurora.org> > Cc: Arnd Bergmann <arnd@arndb.de> > Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> > Signed-off-by: Rob Herring <robh@kernel.org> > Signed-off-by: Stefan Agner <stefan@agner.ch> > --- > Changes since v2: > - Rebased and tested on rmk/for-next > - Flush TLB's when clearing the temporary PMD in early_fixmap_shutdown > > Changes since v1 (RFC): > - Rebased and tested on v3.19-rc5 > - Spelling errors in comments and commit message > - Added Rob's SOB > > arch/arm/Kconfig | 3 ++ > arch/arm/include/asm/fixmap.h | 13 +++++++- > arch/arm/kernel/setup.c | 3 ++ > arch/arm/mm/highmem.c | 6 ++-- > arch/arm/mm/mmu.c | 78 +++++++++++++++++++++++++++++++++++++++++-- > 5 files changed, 96 insertions(+), 7 deletions(-) > > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig > index d0950ce..ef164a7 100644 > --- a/arch/arm/Kconfig > +++ b/arch/arm/Kconfig > @@ -186,6 +186,9 @@ config ARCH_HAS_ILOG2_U64 > config ARCH_HAS_BANDGAP > bool > > +config FIX_EARLYCON_MEM > + def_bool y > + > config GENERIC_HWEIGHT > bool > default y > diff --git a/arch/arm/include/asm/fixmap.h b/arch/arm/include/asm/fixmap.h > index 0415eae..2d8b12b 100644 > --- a/arch/arm/include/asm/fixmap.h > +++ b/arch/arm/include/asm/fixmap.h > @@ -6,9 +6,13 @@ > #define FIXADDR_TOP (FIXADDR_END - PAGE_SIZE) > > #include <asm/kmap_types.h> > +#include <asm/pgtable.h> > > enum fixed_addresses { > - FIX_KMAP_BEGIN, > + FIX_EARLYCON_MEM_BASE, > + __end_of_permanent_fixed_addresses, > + > + FIX_KMAP_BEGIN = __end_of_permanent_fixed_addresses, > FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_TYPE_NR * NR_CPUS) - 1, > > /* Support writing RO kernel text via kprobes, jump labels, etc. */ > @@ -18,7 +22,14 @@ enum fixed_addresses { > __end_of_fixed_addresses > }; > > +#define FIXMAP_PAGE_COMMON (L_PTE_YOUNG | L_PTE_PRESENT | L_PTE_XN | L_PTE_DIRTY) > + > +#define FIXMAP_PAGE_NORMAL (FIXMAP_PAGE_COMMON | L_PTE_MT_WRITEBACK) > +#define FIXMAP_PAGE_IO (FIXMAP_PAGE_COMMON | L_PTE_MT_DEV_SHARED | L_PTE_SHARED) > +#define FIXMAP_PAGE_NOCACHE FIXMAP_PAGE_IO I'm really not happy with this. What are the expected semantics of the set_fixmap_nocache() and set_fixmap_offset_nocache() ? Are they there for mapping a device, or are they there for mapping _memory_ ? I would prefer that FIXMAP_PAGE_NOCACHE is _not_ provided until the semantics of that can be clarified, as there is a difference between the two on ARM. > + > void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot); > +void __init early_fixmap_init(void); > > #include <asm-generic/fixmap.h> > > diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c > index e6d8c76..6f03beb 100644 > --- a/arch/arm/kernel/setup.c > +++ b/arch/arm/kernel/setup.c > @@ -37,6 +37,7 @@ > #include <asm/cpu.h> > #include <asm/cputype.h> > #include <asm/elf.h> > +#include <asm/fixmap.h> > #include <asm/procinfo.h> > #include <asm/psci.h> > #include <asm/sections.h> > @@ -953,6 +954,8 @@ void __init setup_arch(char **cmdline_p) > strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE); > *cmdline_p = cmd_line; > > + early_fixmap_init(); > + > parse_early_param(); > > #ifdef CONFIG_MMU > diff --git a/arch/arm/mm/highmem.c b/arch/arm/mm/highmem.c > index b98895d..c7097f9 100644 > --- a/arch/arm/mm/highmem.c > +++ b/arch/arm/mm/highmem.c > @@ -78,7 +78,7 @@ void *kmap_atomic(struct page *page) > > type = kmap_atomic_idx_push(); > > - idx = type + KM_TYPE_NR * smp_processor_id(); > + idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id(); > vaddr = __fix_to_virt(idx); > #ifdef CONFIG_DEBUG_HIGHMEM > /* > @@ -105,7 +105,7 @@ void __kunmap_atomic(void *kvaddr) > > if (kvaddr >= (void *)FIXADDR_START) { > type = kmap_atomic_idx(); > - idx = type + KM_TYPE_NR * smp_processor_id(); > + idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id(); > > if (cache_is_vivt()) > __cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE); > @@ -135,7 +135,7 @@ void *kmap_atomic_pfn(unsigned long pfn) > return page_address(page); > > type = kmap_atomic_idx_push(); > - idx = type + KM_TYPE_NR * smp_processor_id(); > + idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id(); > vaddr = __fix_to_virt(idx); > #ifdef CONFIG_DEBUG_HIGHMEM > BUG_ON(!pte_none(get_fixmap_pte(vaddr))); > diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c > index 6ca7d9a..d3e9702 100644 > --- a/arch/arm/mm/mmu.c > +++ b/arch/arm/mm/mmu.c > @@ -357,6 +357,47 @@ const struct mem_type *get_mem_type(unsigned int type) > } > EXPORT_SYMBOL(get_mem_type); > > +static pte_t *(*pte_offset_fixmap)(pmd_t *dir, unsigned long addr); > + > +static pte_t bm_pte[PTRS_PER_PTE + PTE_HWTABLE_PTRS] > + __aligned(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE) __initdata; > + > +static pte_t * __init pte_offset_early_fixmap(pmd_t *dir, unsigned long addr) > +{ > + return &bm_pte[pte_index(addr)]; > +} > + > +static pte_t *pte_offset_late_fixmap(pmd_t *dir, unsigned long addr) > +{ > + return pte_offset_kernel(dir, addr); > +} > + > +static inline pmd_t * __init fixmap_pmd(unsigned long addr) > +{ > + pgd_t *pgd = pgd_offset_k(addr); > + pud_t *pud = pud_offset(pgd, addr); > + pmd_t *pmd = pmd_offset(pud, addr); > + > + return pmd; > +} > + > +void __init early_fixmap_init(void) > +{ > + pmd_t *pmd; > + > + /* > + * The early fixmap range spans multiple pmds, for which > + * we are not prepared: > + */ > + BUILD_BUG_ON((__fix_to_virt(__end_of_permanent_fixed_addresses) >> PMD_SHIFT) > + != FIXADDR_TOP >> PMD_SHIFT); > + > + pmd = fixmap_pmd(FIXADDR_TOP); > + pmd_populate_kernel(&init_mm, pmd, bm_pte); > + > + pte_offset_fixmap = &pte_offset_early_fixmap; Please don't use "address-of function" in the kernel, it's really not necessary and is just plain confusing. We don't use it elsewhere, so there's no reason to be different. > +} > + > /* > * To avoid TLB flush broadcasts, this uses local_flush_tlb_kernel_range(). > * As a result, this can only be called with preemption disabled, as under > @@ -365,7 +406,7 @@ EXPORT_SYMBOL(get_mem_type); > void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot) > { > unsigned long vaddr = __fix_to_virt(idx); > - pte_t *pte = pte_offset_kernel(pmd_off_k(vaddr), vaddr); > + pte_t *pte = pte_offset_fixmap(pmd_off_k(vaddr), vaddr); > > /* Make sure fixmap region does not exceed available allocation. */ > BUILD_BUG_ON(FIXADDR_START + (__end_of_fixed_addresses * PAGE_SIZE) > > @@ -855,7 +896,7 @@ static void __init create_mapping(struct map_desc *md) > } > > if ((md->type == MT_DEVICE || md->type == MT_ROM) && > - md->virtual >= PAGE_OFFSET && > + md->virtual >= PAGE_OFFSET && md->virtual < FIXADDR_START && > (md->virtual < VMALLOC_START || md->virtual >= VMALLOC_END)) { > pr_warn("BUG: mapping for 0x%08llx at 0x%08lx out of vmalloc space\n", > (long long)__pfn_to_phys((u64)md->pfn), md->virtual); > @@ -1231,7 +1272,7 @@ static void __init devicemaps_init(const struct machine_desc *mdesc) > > early_trap_init(vectors); > > - for (addr = VMALLOC_START; addr; addr += PMD_SIZE) > + for (addr = VMALLOC_START; addr < FIXADDR_START; addr += PMD_SIZE) > pmd_clear(pmd_off_k(addr)); You introduce a bug here - we no logner clear the very top entry of the page tables, which means it could contain anything - and means that the subsequent creation of the L2 table in early_pte_alloc() can fail. > > /* > @@ -1483,6 +1524,36 @@ void __init early_paging_init(const struct machine_desc *mdesc) > > #endif > > +static void __init early_fixmap_shutdown(void) > +{ > + int i; > + unsigned long va = fix_to_virt(__end_of_permanent_fixed_addresses - 1); > + > + pte_offset_fixmap = &pte_offset_late_fixmap; Again, please don't use "address-of function". > + pmd_clear(fixmap_pmd(va)); > + local_flush_tlb_kernel_page(va); > + > + for (i = 0; i < __end_of_permanent_fixed_addresses; i++) { > + pte_t *pte; > + struct map_desc map; > + > + map.virtual = fix_to_virt(i); > + pte = pte_offset_early_fixmap(pmd_off_k(map.virtual), map.virtual); > + > + /* Only i/o device mappings are supported ATM */ > + if (pte_none(*pte) || > + (pte_val(*pte) & L_PTE_MT_MASK) != L_PTE_MT_DEV_SHARED) > + continue; > + > + map.pfn = pte_pfn(*pte); > + map.type = MT_DEVICE; > + map.length = PAGE_SIZE; > + > + create_mapping(&map); > + } > +} > + > + > /* > * paging_init() sets up the page tables, initialises the zone memory > * maps, and sets up the zero page, bad page and bad page tables. > @@ -1495,6 +1566,7 @@ void __init paging_init(const struct machine_desc *mdesc) > prepare_page_table(); > map_lowmem(); > dma_contiguous_remap(); > + early_fixmap_shutdown(); > devicemaps_init(mdesc); > kmap_init(); > tcm_init(); > -- > 2.4.2 >
On 2015-06-06 14:48, Russell King - ARM Linux wrote: > On Sat, Jun 06, 2015 at 02:31:28PM +0200, Stefan Agner wrote: <snip> >> --- a/arch/arm/include/asm/fixmap.h >> +++ b/arch/arm/include/asm/fixmap.h >> @@ -6,9 +6,13 @@ >> #define FIXADDR_TOP (FIXADDR_END - PAGE_SIZE) >> >> #include <asm/kmap_types.h> >> +#include <asm/pgtable.h> >> >> enum fixed_addresses { >> - FIX_KMAP_BEGIN, >> + FIX_EARLYCON_MEM_BASE, >> + __end_of_permanent_fixed_addresses, >> + >> + FIX_KMAP_BEGIN = __end_of_permanent_fixed_addresses, >> FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_TYPE_NR * NR_CPUS) - 1, >> >> /* Support writing RO kernel text via kprobes, jump labels, etc. */ >> @@ -18,7 +22,14 @@ enum fixed_addresses { >> __end_of_fixed_addresses >> }; >> >> +#define FIXMAP_PAGE_COMMON (L_PTE_YOUNG | L_PTE_PRESENT | L_PTE_XN | L_PTE_DIRTY) >> + >> +#define FIXMAP_PAGE_NORMAL (FIXMAP_PAGE_COMMON | L_PTE_MT_WRITEBACK) >> +#define FIXMAP_PAGE_IO (FIXMAP_PAGE_COMMON | L_PTE_MT_DEV_SHARED | L_PTE_SHARED) >> +#define FIXMAP_PAGE_NOCACHE FIXMAP_PAGE_IO > > I'm really not happy with this. What are the expected semantics of the > set_fixmap_nocache() and set_fixmap_offset_nocache() ? Are they there > for mapping a device, or are they there for mapping _memory_ ? > > I would prefer that FIXMAP_PAGE_NOCACHE is _not_ provided until the > semantics of that can be clarified, as there is a difference between the > two on ARM. set_fixmap_nocache is used for PCIe and IO APIC stuff, so actually IO devices. However, as far as I understand the x86 page table definitions in pgtable_types.h, IO devices are not explicitly uncached on x86. The define set_fixmap_nocache make sure IO devices are mapped uncached... Maybe set_fixmap_io_nocache instead of set_fixmap_nocache would be more appropriate...? Maybe the initial authors of the common fixmap.h have a more clear understanding of the expected semantics, Mark? Since the focus currently is on set_fixmap_io, we could define FIXMAP_PAGE_IO only for the time being...? Agreed to the rest, will apply the review in v4. -- Stefan
On Sat, Jun 6, 2015 at 8:29 AM, Stefan Agner <stefan@agner.ch> wrote: > On 2015-06-06 14:48, Russell King - ARM Linux wrote: >> On Sat, Jun 06, 2015 at 02:31:28PM +0200, Stefan Agner wrote: > <snip> >>> --- a/arch/arm/include/asm/fixmap.h >>> +++ b/arch/arm/include/asm/fixmap.h >>> @@ -6,9 +6,13 @@ >>> #define FIXADDR_TOP (FIXADDR_END - PAGE_SIZE) >>> >>> #include <asm/kmap_types.h> >>> +#include <asm/pgtable.h> >>> >>> enum fixed_addresses { >>> - FIX_KMAP_BEGIN, >>> + FIX_EARLYCON_MEM_BASE, >>> + __end_of_permanent_fixed_addresses, >>> + >>> + FIX_KMAP_BEGIN = __end_of_permanent_fixed_addresses, >>> FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_TYPE_NR * NR_CPUS) - 1, >>> >>> /* Support writing RO kernel text via kprobes, jump labels, etc. */ >>> @@ -18,7 +22,14 @@ enum fixed_addresses { >>> __end_of_fixed_addresses >>> }; >>> >>> +#define FIXMAP_PAGE_COMMON (L_PTE_YOUNG | L_PTE_PRESENT | L_PTE_XN | L_PTE_DIRTY) >>> + >>> +#define FIXMAP_PAGE_NORMAL (FIXMAP_PAGE_COMMON | L_PTE_MT_WRITEBACK) >>> +#define FIXMAP_PAGE_IO (FIXMAP_PAGE_COMMON | L_PTE_MT_DEV_SHARED | L_PTE_SHARED) >>> +#define FIXMAP_PAGE_NOCACHE FIXMAP_PAGE_IO >> >> I'm really not happy with this. What are the expected semantics of the >> set_fixmap_nocache() and set_fixmap_offset_nocache() ? Are they there >> for mapping a device, or are they there for mapping _memory_ ? >> >> I would prefer that FIXMAP_PAGE_NOCACHE is _not_ provided until the >> semantics of that can be clarified, as there is a difference between the >> two on ARM. > > set_fixmap_nocache is used for PCIe and IO APIC stuff, so actually IO > devices. However, as far as I understand the x86 page table definitions > in pgtable_types.h, IO devices are not explicitly uncached on x86. The > define set_fixmap_nocache make sure IO devices are mapped uncached... > Maybe set_fixmap_io_nocache instead of set_fixmap_nocache would be more > appropriate...? > > Maybe the initial authors of the common fixmap.h have a more clear > understanding of the expected semantics, Mark? They should be the same as ioremap and ioremap_nocache which are aliases of each other on ARM. If you want RAM then you should be using FIXMAP_PAGE_NORMAL. Perhaps we can come up with some error checking here. > Since the focus currently is on set_fixmap_io, we could define > FIXMAP_PAGE_IO only for the time being...? We could, but I think it is better to sort this out now. Rob
On Sat, 2015-06-06 at 10:29 -0500, Rob Herring wrote: > On Sat, Jun 6, 2015 at 8:29 AM, Stefan Agner <stefan@agner.ch> wrote: > > On 2015-06-06 14:48, Russell King - ARM Linux wrote: > >> On Sat, Jun 06, 2015 at 02:31:28PM +0200, Stefan Agner wrote: > > <snip> > >>> --- a/arch/arm/include/asm/fixmap.h > >>> +++ b/arch/arm/include/asm/fixmap.h > >>> @@ -6,9 +6,13 @@ > >>> #define FIXADDR_TOP (FIXADDR_END - PAGE_SIZE) > >>> > >>> #include <asm/kmap_types.h> > >>> +#include <asm/pgtable.h> > >>> > >>> enum fixed_addresses { > >>> - FIX_KMAP_BEGIN, > >>> + FIX_EARLYCON_MEM_BASE, > >>> + __end_of_permanent_fixed_addresses, > >>> + > >>> + FIX_KMAP_BEGIN = __end_of_permanent_fixed_addresses, > >>> FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_TYPE_NR * NR_CPUS) - 1, > >>> > >>> /* Support writing RO kernel text via kprobes, jump labels, etc. */ > >>> @@ -18,7 +22,14 @@ enum fixed_addresses { > >>> __end_of_fixed_addresses > >>> }; > >>> > >>> +#define FIXMAP_PAGE_COMMON (L_PTE_YOUNG | L_PTE_PRESENT | L_PTE_XN | L_PTE_DIRTY) > >>> + > >>> +#define FIXMAP_PAGE_NORMAL (FIXMAP_PAGE_COMMON | L_PTE_MT_WRITEBACK) > >>> +#define FIXMAP_PAGE_IO (FIXMAP_PAGE_COMMON | L_PTE_MT_DEV_SHARED | L_PTE_SHARED) > >>> +#define FIXMAP_PAGE_NOCACHE FIXMAP_PAGE_IO > >> > >> I'm really not happy with this. What are the expected semantics of the > >> set_fixmap_nocache() and set_fixmap_offset_nocache() ? Are they there > >> for mapping a device, or are they there for mapping _memory_ ? > >> > >> I would prefer that FIXMAP_PAGE_NOCACHE is _not_ provided until the > >> semantics of that can be clarified, as there is a difference between the > >> two on ARM. > > > > set_fixmap_nocache is used for PCIe and IO APIC stuff, so actually IO > > devices. However, as far as I understand the x86 page table definitions > > in pgtable_types.h, IO devices are not explicitly uncached on x86. The > > define set_fixmap_nocache make sure IO devices are mapped uncached... > > Maybe set_fixmap_io_nocache instead of set_fixmap_nocache would be more > > appropriate...? > > > > Maybe the initial authors of the common fixmap.h have a more clear > > understanding of the expected semantics, Mark? > > They should be the same as ioremap and ioremap_nocache which are > aliases of each other on ARM. If you want RAM then you should be using > FIXMAP_PAGE_NORMAL. Perhaps we can come up with some error checking > here. > > > Since the focus currently is on set_fixmap_io, we could define > > FIXMAP_PAGE_IO only for the time being...? > > We could, but I think it is better to sort this out now. > I agree with Rob here. AFAICT, x86 is the only arch which actually uses set_fixmap_nocache. Other arches created definitions for it, but all uses appear to depend on CONFIG_X86.
On Sat, Jun 06, 2015 at 01:48:00PM +0100, Russell King - ARM Linux wrote: > On Sat, Jun 06, 2015 at 02:31:28PM +0200, Stefan Agner wrote: > > @@ -1231,7 +1272,7 @@ static void __init devicemaps_init(const struct machine_desc *mdesc) > > > > early_trap_init(vectors); > > > > - for (addr = VMALLOC_START; addr; addr += PMD_SIZE) > > + for (addr = VMALLOC_START; addr < FIXADDR_START; addr += PMD_SIZE) > > pmd_clear(pmd_off_k(addr)); > > You introduce a bug here - we no logner clear the very top entry of the > page tables, which means it could contain anything - and means that the > subsequent creation of the L2 table in early_pte_alloc() can fail. You trimmed my reply hard, and seemingly ignored this; the v4 patch contains exactly the same code here, and therefore continues to leave the top of the page table uninitialised. Not applying the v4 patch, sorry.
On 2015-07-10 13:25, Russell King - ARM Linux wrote: > On Sat, Jun 06, 2015 at 01:48:00PM +0100, Russell King - ARM Linux wrote: >> On Sat, Jun 06, 2015 at 02:31:28PM +0200, Stefan Agner wrote: >> > @@ -1231,7 +1272,7 @@ static void __init devicemaps_init(const struct machine_desc *mdesc) >> > >> > early_trap_init(vectors); >> > >> > - for (addr = VMALLOC_START; addr; addr += PMD_SIZE) >> > + for (addr = VMALLOC_START; addr < FIXADDR_START; addr += PMD_SIZE) >> > pmd_clear(pmd_off_k(addr)); >> >> You introduce a bug here - we no logner clear the very top entry of the >> page tables, which means it could contain anything - and means that the >> subsequent creation of the L2 table in early_pte_alloc() can fail. > > You trimmed my reply hard, and seemingly ignored this; the v4 patch > contains exactly the same code here, and therefore continues to leave > the top of the page table uninitialised. Maybe I get something wrong here, but the very top PMD is left out on purpose, since early_fixmap_shutdown takes care of that. However v3 also did not clear the second to top PMD. I thought I did take care of that by using this slightly different for loop in v4: for (addr = VMALLOC_START; addr < (FIXADDR_TOP & PMD_MASK); addr += PMD_SIZE) However, looking now at the code I'm not sure if this is right either. I think what is needed is addr < (FIXADDR_TOP & ~PMD_MASK). This will make sure that only the top PMD is not cleared, which is taken care of by early fixmap... -- Stefan
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index d0950ce..ef164a7 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -186,6 +186,9 @@ config ARCH_HAS_ILOG2_U64 config ARCH_HAS_BANDGAP bool +config FIX_EARLYCON_MEM + def_bool y + config GENERIC_HWEIGHT bool default y diff --git a/arch/arm/include/asm/fixmap.h b/arch/arm/include/asm/fixmap.h index 0415eae..2d8b12b 100644 --- a/arch/arm/include/asm/fixmap.h +++ b/arch/arm/include/asm/fixmap.h @@ -6,9 +6,13 @@ #define FIXADDR_TOP (FIXADDR_END - PAGE_SIZE) #include <asm/kmap_types.h> +#include <asm/pgtable.h> enum fixed_addresses { - FIX_KMAP_BEGIN, + FIX_EARLYCON_MEM_BASE, + __end_of_permanent_fixed_addresses, + + FIX_KMAP_BEGIN = __end_of_permanent_fixed_addresses, FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_TYPE_NR * NR_CPUS) - 1, /* Support writing RO kernel text via kprobes, jump labels, etc. */ @@ -18,7 +22,14 @@ enum fixed_addresses { __end_of_fixed_addresses }; +#define FIXMAP_PAGE_COMMON (L_PTE_YOUNG | L_PTE_PRESENT | L_PTE_XN | L_PTE_DIRTY) + +#define FIXMAP_PAGE_NORMAL (FIXMAP_PAGE_COMMON | L_PTE_MT_WRITEBACK) +#define FIXMAP_PAGE_IO (FIXMAP_PAGE_COMMON | L_PTE_MT_DEV_SHARED | L_PTE_SHARED) +#define FIXMAP_PAGE_NOCACHE FIXMAP_PAGE_IO + void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot); +void __init early_fixmap_init(void); #include <asm-generic/fixmap.h> diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index e6d8c76..6f03beb 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -37,6 +37,7 @@ #include <asm/cpu.h> #include <asm/cputype.h> #include <asm/elf.h> +#include <asm/fixmap.h> #include <asm/procinfo.h> #include <asm/psci.h> #include <asm/sections.h> @@ -953,6 +954,8 @@ void __init setup_arch(char **cmdline_p) strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE); *cmdline_p = cmd_line; + early_fixmap_init(); + parse_early_param(); #ifdef CONFIG_MMU diff --git a/arch/arm/mm/highmem.c b/arch/arm/mm/highmem.c index b98895d..c7097f9 100644 --- a/arch/arm/mm/highmem.c +++ b/arch/arm/mm/highmem.c @@ -78,7 +78,7 @@ void *kmap_atomic(struct page *page) type = kmap_atomic_idx_push(); - idx = type + KM_TYPE_NR * smp_processor_id(); + idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id(); vaddr = __fix_to_virt(idx); #ifdef CONFIG_DEBUG_HIGHMEM /* @@ -105,7 +105,7 @@ void __kunmap_atomic(void *kvaddr) if (kvaddr >= (void *)FIXADDR_START) { type = kmap_atomic_idx(); - idx = type + KM_TYPE_NR * smp_processor_id(); + idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id(); if (cache_is_vivt()) __cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE); @@ -135,7 +135,7 @@ void *kmap_atomic_pfn(unsigned long pfn) return page_address(page); type = kmap_atomic_idx_push(); - idx = type + KM_TYPE_NR * smp_processor_id(); + idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id(); vaddr = __fix_to_virt(idx); #ifdef CONFIG_DEBUG_HIGHMEM BUG_ON(!pte_none(get_fixmap_pte(vaddr))); diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index 6ca7d9a..d3e9702 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -357,6 +357,47 @@ const struct mem_type *get_mem_type(unsigned int type) } EXPORT_SYMBOL(get_mem_type); +static pte_t *(*pte_offset_fixmap)(pmd_t *dir, unsigned long addr); + +static pte_t bm_pte[PTRS_PER_PTE + PTE_HWTABLE_PTRS] + __aligned(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE) __initdata; + +static pte_t * __init pte_offset_early_fixmap(pmd_t *dir, unsigned long addr) +{ + return &bm_pte[pte_index(addr)]; +} + +static pte_t *pte_offset_late_fixmap(pmd_t *dir, unsigned long addr) +{ + return pte_offset_kernel(dir, addr); +} + +static inline pmd_t * __init fixmap_pmd(unsigned long addr) +{ + pgd_t *pgd = pgd_offset_k(addr); + pud_t *pud = pud_offset(pgd, addr); + pmd_t *pmd = pmd_offset(pud, addr); + + return pmd; +} + +void __init early_fixmap_init(void) +{ + pmd_t *pmd; + + /* + * The early fixmap range spans multiple pmds, for which + * we are not prepared: + */ + BUILD_BUG_ON((__fix_to_virt(__end_of_permanent_fixed_addresses) >> PMD_SHIFT) + != FIXADDR_TOP >> PMD_SHIFT); + + pmd = fixmap_pmd(FIXADDR_TOP); + pmd_populate_kernel(&init_mm, pmd, bm_pte); + + pte_offset_fixmap = &pte_offset_early_fixmap; +} + /* * To avoid TLB flush broadcasts, this uses local_flush_tlb_kernel_range(). * As a result, this can only be called with preemption disabled, as under @@ -365,7 +406,7 @@ EXPORT_SYMBOL(get_mem_type); void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot) { unsigned long vaddr = __fix_to_virt(idx); - pte_t *pte = pte_offset_kernel(pmd_off_k(vaddr), vaddr); + pte_t *pte = pte_offset_fixmap(pmd_off_k(vaddr), vaddr); /* Make sure fixmap region does not exceed available allocation. */ BUILD_BUG_ON(FIXADDR_START + (__end_of_fixed_addresses * PAGE_SIZE) > @@ -855,7 +896,7 @@ static void __init create_mapping(struct map_desc *md) } if ((md->type == MT_DEVICE || md->type == MT_ROM) && - md->virtual >= PAGE_OFFSET && + md->virtual >= PAGE_OFFSET && md->virtual < FIXADDR_START && (md->virtual < VMALLOC_START || md->virtual >= VMALLOC_END)) { pr_warn("BUG: mapping for 0x%08llx at 0x%08lx out of vmalloc space\n", (long long)__pfn_to_phys((u64)md->pfn), md->virtual); @@ -1231,7 +1272,7 @@ static void __init devicemaps_init(const struct machine_desc *mdesc) early_trap_init(vectors); - for (addr = VMALLOC_START; addr; addr += PMD_SIZE) + for (addr = VMALLOC_START; addr < FIXADDR_START; addr += PMD_SIZE) pmd_clear(pmd_off_k(addr)); /* @@ -1483,6 +1524,36 @@ void __init early_paging_init(const struct machine_desc *mdesc) #endif +static void __init early_fixmap_shutdown(void) +{ + int i; + unsigned long va = fix_to_virt(__end_of_permanent_fixed_addresses - 1); + + pte_offset_fixmap = &pte_offset_late_fixmap; + pmd_clear(fixmap_pmd(va)); + local_flush_tlb_kernel_page(va); + + for (i = 0; i < __end_of_permanent_fixed_addresses; i++) { + pte_t *pte; + struct map_desc map; + + map.virtual = fix_to_virt(i); + pte = pte_offset_early_fixmap(pmd_off_k(map.virtual), map.virtual); + + /* Only i/o device mappings are supported ATM */ + if (pte_none(*pte) || + (pte_val(*pte) & L_PTE_MT_MASK) != L_PTE_MT_DEV_SHARED) + continue; + + map.pfn = pte_pfn(*pte); + map.type = MT_DEVICE; + map.length = PAGE_SIZE; + + create_mapping(&map); + } +} + + /* * paging_init() sets up the page tables, initialises the zone memory * maps, and sets up the zero page, bad page and bad page tables. @@ -1495,6 +1566,7 @@ void __init paging_init(const struct machine_desc *mdesc) prepare_page_table(); map_lowmem(); dma_contiguous_remap(); + early_fixmap_shutdown(); devicemaps_init(mdesc); kmap_init(); tcm_init();