[PATCH v2] ARM: Introduce patching of phys_to_virt and vice versa

Nicolas Pitre nico at fluxnic.net
Fri Oct 8 09:36:40 EDT 2010


On Fri, 8 Oct 2010, Eric Miao wrote:

> ** In this v2 version, the __volatile__ keyword of the patch stub assembly is
> removed. That prevents gcc from doing a good optimization. The test build
                  ^^^

You mean "prevented".

> result showed an acceptable optimized code generation.
> 
> In most cases, the delta between PHYS_OFFSET and PAGE_OFFSET is normally
> 16MiB aligned, which means the difference can be handled by a simple ADD
> or SUB instruction with an immediate shift operand in ARM.  This will be
> a bit more efficient and generic when PHYS_OFFSET goes run-time.
> 
> This idea can be made generic to allow conversions more than phys_to_virt
> and virt_to_phys. A stub instruction is inserted where applicable, and it
> has a form of 'add rn, rd, #imm', where the lowest 8-bit of #imm is used
> to identify the type of patching.  Currently, only two types are defined,
> but could be expanded in my POV to definitions like __io(), __mem_pci()
> and so on. A __patch_table section is introduced to include the addresses
> of all these stub instructions.
> 
> There are several places for improvement:
> 
> 1. constant parameters which can be optimized by the compiler now needs
>    one additional instruction (although the optimization is neither
>    possible when PHYS_OFFSET goes a variable)
> 
> 2. flush_cache_all() when patching done is brute but simple enough here,
>    provided it's done only once during startup.
> 
> 3. thumb2 can be supported in a same way, but will leave that for future
>    enhancement.
> 
> The general idea comes from Nicolas Pitre, and is drafted at
>     https://wiki.ubuntu.com/Specs/ARMSingleKernel
> 
> Signed-off-by: Nicolas Pitre <nicolas.pitre at canonical.com>
> Signed-off-by: Eric Miao <eric.miao at canonical.com>
> ---
>  arch/arm/Kconfig              |   10 ++++++++
>  arch/arm/include/asm/memory.h |   32 ++++++++++++++++++++++++++
>  arch/arm/kernel/setup.c       |   50 +++++++++++++++++++++++++++++++++++++++++
>  arch/arm/kernel/vmlinux.lds.S |    4 +++
>  4 files changed, 96 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 553b7cf..5c856d9 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -187,6 +187,16 @@ config VECTORS_BASE
>  	help
>  	  The base address of exception vectors.
> 
> +config ARM_PATCH_PHYS_VIRT
> +	def_bool n

No need for this as the default is already n.

> +	help
> +	  Note this is only for non-XIP and non-Thumb2 kernels. And there
> +	  is CPU support which needs to read data in order to writeback
> +	  dirty entries in the cache. (e.g. StrongARM, ebsa110, footbridge,
> +	  rpc, sa1100, and shark). The mappings in the above cases do not
> +	  exist before paging_init() has completed. Thus this option does
> +	  not support these CPUs at this moment.
> +
>  source "init/Kconfig"
> 
>  source "kernel/Kconfig.freezer"
> diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
> index 23c2e8e..9cf6d3f 100644
> --- a/arch/arm/include/asm/memory.h
> +++ b/arch/arm/include/asm/memory.h
> @@ -182,6 +182,37 @@
>   */
>  #define PHYS_PFN_OFFSET	(PHYS_OFFSET >> PAGE_SHIFT)
> 
> +#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
> +
> +#define PATCH_TYPE_PHYS_TO_VIRT		(0)
> +#define PATCH_TYPE_VIRT_TO_PHYS		(1)
> +
> +#define __patch_stub(from,to,type)			\
> +	__asm__(					\
> +	"1:	add	%0, %1, %2\n"			\
> +	"\n"						\
> +	"	.pushsection __patch_table,\"a\"\n"	\
> +	"	.long	1b\n"				\
> +	"	.popsection\n"				\
> +	: "=r" (to)					\
> +	: "r" (from), "I" (type))
> +
> +static inline unsigned long virt_to_phys(void *x)
> +{
> +	unsigned long t;
> +
> +	__patch_stub(x, t, PATCH_TYPE_VIRT_TO_PHYS);
> +	return t;
> +}
> +
> +static inline void *phys_to_virt(unsigned long x)
> +{
> +	void *t;
> +
> +	__patch_stub(x, t, PATCH_TYPE_PHYS_TO_VIRT);
> +	return t;
> +}
> +#else
>  /*
>   * These are *only* valid on the kernel direct mapped RAM memory.
>   * Note: Drivers should NOT use these.  They are the wrong
> @@ -197,6 +228,7 @@ static inline void *phys_to_virt(unsigned long x)
>  {
>  	return (void *)(__phys_to_virt((unsigned long)(x)));
>  }
> +#endif
> 
>  /*
>   * Drivers should NOT use these either.
> diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
> index d5231ae..6d024ad 100644
> --- a/arch/arm/kernel/setup.c
> +++ b/arch/arm/kernel/setup.c
> @@ -764,12 +764,62 @@ static void __init squash_mem_tags(struct tag *tag)
>  			tag->hdr.tag = ATAG_NONE;
>  }
> 
> +#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
> +
> +#define PATCH_INSTR_ADD		(0x00800000)
> +#define PATCH_INSTR_SUB		(0x00400000)
> +
> +#define PATCH_STUB_MASK		(0xffe000ff)
> +#define PATCH_STUB_PHYS_TO_VIRT	(0xe2800000 | PATCH_TYPE_PHYS_TO_VIRT)
> +#define PATCH_STUB_VIRT_TO_PHYS	(0xe2800000 | PATCH_TYPE_VIRT_TO_PHYS)
> +
> +/* patch_phys_virt - patch the stub instructions with the delta between
> + * PHYS_OFFSET and PAGE_OFFSET, which is assumed to be 16MiB aligned and
> + * can be expressed by an immediate shifter operand. The stub instruction
> + * has a form of 'add rd, rn, #imm', where the lowest 8-bit of #imm is
> + * used to identify the type of patching.
> + */
> +static void __init patch_phys_virt(void)
> +{
> +	extern unsigned int *__patch_table_begin, *__patch_table_end;
> +	unsigned int **p;
> +	unsigned int imm, instr[2];
> +
> +	if (PHYS_OFFSET & 0x00ffffff)
> +		panic("Physical memory start is not 16MiB aligned\n");
> +
> +	if (likely(PHYS_OFFSET < PAGE_OFFSET)) {
> +		imm = 0x400 | ((PAGE_OFFSET >> 24) - (PHYS_OFFSET >> 24));
> +		instr[0] = PATCH_INSTR_ADD | imm;
> +		instr[1] = PATCH_INSTR_SUB | imm;
> +	} else {
> +		imm = 0x400 | ((PHYS_OFFSET >> 24) - (PAGE_OFFSET >> 24));
> +		instr[0] = PATCH_INSTR_SUB | imm;
> +		instr[1] = PATCH_INSTR_ADD | imm;
> +	}
> +
> +	for (p = &__patch_table_begin; p < &__patch_table_end; p++) {
> +		unsigned int *inptr = *p;
> +
> +		if ((*inptr & PATCH_STUB_MASK) == PATCH_STUB_PHYS_TO_VIRT)
> +			*inptr = (*inptr & ~0x00e00fff) | instr[0];
> +		if ((*inptr & PATCH_STUB_MASK) == PATCH_STUB_VIRT_TO_PHYS)
> +			*inptr = (*inptr & ~0x00e00fff) | instr[1];
> +	}
> +	flush_cache_all();
> +}
> +#else
> +static inline void patch_phys_virt(void) {}
> +#endif
> +
>  void __init setup_arch(char **cmdline_p)
>  {
>  	struct tag *tags = (struct tag *)&init_tags;
>  	struct machine_desc *mdesc;
>  	char *from = default_command_line;
> 
> +	patch_phys_virt();
> +
>  	unwind_init();
> 
>  	setup_processor();
> diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
> index b16c079..c48c754 100644
> --- a/arch/arm/kernel/vmlinux.lds.S
> +++ b/arch/arm/kernel/vmlinux.lds.S
> @@ -41,6 +41,10 @@ SECTIONS
>  			*(.taglist.init)
>  		__tagtable_end = .;
> 
> +		__patch_table_begin = .;
> +			*(__patch_table)
> +		__patch_table_end = .;
> +
>  		INIT_SETUP(16)
> 
>  		INIT_CALLS
> -- 
> 1.7.0.4
> 



More information about the linux-arm-kernel mailing list