diff mbox series

[v2,12/13] armv8: layerscape: relocate spin table if EFI_LOADER is enabled

Message ID 20200601195336.3237-13-michael@walle.cc
State Accepted
Commit 16863da82ad1df862778f42a8cb2500d6fd843c2
Headers show
Series armv8: layerscape: spin table relocation fixes and cleanups | expand

Commit Message

Michael Walle June 1, 2020, 7:53 p.m. UTC
On ARM64, a 64kb region is reserved for the runtime services code.
Unfortunately, this code overlaps with the spin table code, which also
needs to be reserved. Thus now that the code is relocatable, allocate a
new page from EFI, copy the spin table code into it, update any pointers
to the old region and the start the secondary CPUs.

Signed-off-by: Michael Walle <michael at walle.cc>
---
 arch/arm/cpu/armv8/fsl-layerscape/mp.c | 36 ++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

Comments

Heinrich Schuchardt June 2, 2020, 8 p.m. UTC | #1
On 6/1/20 9:53 PM, Michael Walle wrote:
> On ARM64, a 64kb region is reserved for the runtime services code.
> Unfortunately, this code overlaps with the spin table code, which also
> needs to be reserved. Thus now that the code is relocatable, allocate a
> new page from EFI, copy the spin table code into it, update any pointers
> to the old region and the start the secondary CPUs.
>
> Signed-off-by: Michael Walle <michael at walle.cc>
> ---
>  arch/arm/cpu/armv8/fsl-layerscape/mp.c | 36 ++++++++++++++++++++++++++
>  1 file changed, 36 insertions(+)
>
> diff --git a/arch/arm/cpu/armv8/fsl-layerscape/mp.c b/arch/arm/cpu/armv8/fsl-layerscape/mp.c
> index d50c5a437b..bd85351705 100644
> --- a/arch/arm/cpu/armv8/fsl-layerscape/mp.c
> +++ b/arch/arm/cpu/armv8/fsl-layerscape/mp.c
> @@ -79,6 +79,10 @@ int fsl_layerscape_wake_seconday_cores(void)
>  	u32 cores, cpu_up_mask = 1;
>  	int i, timeout = 10;
>  	u64 *table;
> +#ifdef CONFIG_EFI_LOADER
> +	u64 reloc_addr = U32_MAX;
> +	efi_status_t ret;
> +#endif
>
>  #ifdef COUNTER_FREQUENCY_REAL
>  	/* update for secondary cores */
> @@ -87,6 +91,38 @@ int fsl_layerscape_wake_seconday_cores(void)
>  			   (unsigned long)&__real_cntfrq + 8);
>  #endif
>
> +#ifdef CONFIG_EFI_LOADER
> +	/*
> +	 * EFI will reserve 64kb for its runtime services. This will probably
> +	 * overlap with our spin table code, which is why we have to relocate
> +	 * it.
> +	 * Keep this after the __real_cntfrq update, so we have it when we
> +	 * copy the complete section here.
> +	 */
> +	ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
> +				 EFI_RESERVED_MEMORY_TYPE,
> +				 efi_size_in_pages(secondary_boot_code_size),
> +				 &reloc_addr);
> +	if (ret == EFI_SUCCESS) {
> +		debug("Relocating spin table from %llx to %llx (size %lx)\n",
> +		      (u64)secondary_boot_code_start, reloc_addr,
> +		      secondary_boot_code_size);
> +		memcpy((void *)reloc_addr, secondary_boot_code_start,
> +		       secondary_boot_code_size);
> +		flush_dcache_range(reloc_addr,
> +				   reloc_addr + secondary_boot_code_size);
> +
> +		/* set new entry point for secondary cores */
> +		secondary_boot_addr += (void *)reloc_addr -
> +				       secondary_boot_code_start;
> +		flush_dcache_range((unsigned long)&secondary_boot_addr,
> +				   (unsigned long)&secondary_boot_addr + 8);

Wouldn't you want to flush the complete target range of memcpy() and
afterwards call invalidate_icache_all(). At least this is what we do in
other cases after copying instructions. Cf. efi_runtime_relocate().

Best regards

Heinrich

> +
> +		/* this will be used to reserve the memory */
> +		secondary_boot_code_start = (void *)reloc_addr;
> +	}
> +#endif
> +
>  	cores = cpu_mask();
>  	/* Clear spin table so that secondary processors
>  	 * observe the correct value after waking up from wfe.
>
Michael Walle June 2, 2020, 8:19 p.m. UTC | #2
Am 2020-06-02 22:00, schrieb Heinrich Schuchardt:
> On 6/1/20 9:53 PM, Michael Walle wrote:
>> On ARM64, a 64kb region is reserved for the runtime services code.
>> Unfortunately, this code overlaps with the spin table code, which also
>> needs to be reserved. Thus now that the code is relocatable, allocate 
>> a
>> new page from EFI, copy the spin table code into it, update any 
>> pointers
>> to the old region and the start the secondary CPUs.
>> 
>> Signed-off-by: Michael Walle <michael at walle.cc>
>> ---
>>  arch/arm/cpu/armv8/fsl-layerscape/mp.c | 36 
>> ++++++++++++++++++++++++++
>>  1 file changed, 36 insertions(+)
>> 
>> diff --git a/arch/arm/cpu/armv8/fsl-layerscape/mp.c 
>> b/arch/arm/cpu/armv8/fsl-layerscape/mp.c
>> index d50c5a437b..bd85351705 100644
>> --- a/arch/arm/cpu/armv8/fsl-layerscape/mp.c
>> +++ b/arch/arm/cpu/armv8/fsl-layerscape/mp.c
>> @@ -79,6 +79,10 @@ int fsl_layerscape_wake_seconday_cores(void)
>>  	u32 cores, cpu_up_mask = 1;
>>  	int i, timeout = 10;
>>  	u64 *table;
>> +#ifdef CONFIG_EFI_LOADER
>> +	u64 reloc_addr = U32_MAX;
>> +	efi_status_t ret;
>> +#endif
>> 
>>  #ifdef COUNTER_FREQUENCY_REAL
>>  	/* update for secondary cores */
>> @@ -87,6 +91,38 @@ int fsl_layerscape_wake_seconday_cores(void)
>>  			   (unsigned long)&__real_cntfrq + 8);
>>  #endif
>> 
>> +#ifdef CONFIG_EFI_LOADER
>> +	/*
>> +	 * EFI will reserve 64kb for its runtime services. This will 
>> probably
>> +	 * overlap with our spin table code, which is why we have to 
>> relocate
>> +	 * it.
>> +	 * Keep this after the __real_cntfrq update, so we have it when we
>> +	 * copy the complete section here.
>> +	 */
>> +	ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
>> +				 EFI_RESERVED_MEMORY_TYPE,
>> +				 efi_size_in_pages(secondary_boot_code_size),
>> +				 &reloc_addr);
>> +	if (ret == EFI_SUCCESS) {
>> +		debug("Relocating spin table from %llx to %llx (size %lx)\n",
>> +		      (u64)secondary_boot_code_start, reloc_addr,
>> +		      secondary_boot_code_size);
>> +		memcpy((void *)reloc_addr, secondary_boot_code_start,
>> +		       secondary_boot_code_size);
>> +		flush_dcache_range(reloc_addr,
>> +				   reloc_addr + secondary_boot_code_size);

dcache flush is done here. but icache is missing, correct.

>> +
>> +		/* set new entry point for secondary cores */
>> +		secondary_boot_addr += (void *)reloc_addr -
>> +				       secondary_boot_code_start;
>> +		flush_dcache_range((unsigned long)&secondary_boot_addr,
>> +				   (unsigned long)&secondary_boot_addr + 8);
> 
> Wouldn't you want to flush the complete target range of memcpy() and
> afterwards call invalidate_icache_all(). At least this is what we do in
> other cases after copying instructions. Cf. efi_runtime_relocate().

see above.

-michael

>> +
>> +		/* this will be used to reserve the memory */
>> +		secondary_boot_code_start = (void *)reloc_addr;
>> +	}
>> +#endif
>> +
>>  	cores = cpu_mask();
>>  	/* Clear spin table so that secondary processors
>>  	 * observe the correct value after waking up from wfe.
>>
diff mbox series

Patch

diff --git a/arch/arm/cpu/armv8/fsl-layerscape/mp.c b/arch/arm/cpu/armv8/fsl-layerscape/mp.c
index d50c5a437b..bd85351705 100644
--- a/arch/arm/cpu/armv8/fsl-layerscape/mp.c
+++ b/arch/arm/cpu/armv8/fsl-layerscape/mp.c
@@ -79,6 +79,10 @@  int fsl_layerscape_wake_seconday_cores(void)
 	u32 cores, cpu_up_mask = 1;
 	int i, timeout = 10;
 	u64 *table;
+#ifdef CONFIG_EFI_LOADER
+	u64 reloc_addr = U32_MAX;
+	efi_status_t ret;
+#endif
 
 #ifdef COUNTER_FREQUENCY_REAL
 	/* update for secondary cores */
@@ -87,6 +91,38 @@  int fsl_layerscape_wake_seconday_cores(void)
 			   (unsigned long)&__real_cntfrq + 8);
 #endif
 
+#ifdef CONFIG_EFI_LOADER
+	/*
+	 * EFI will reserve 64kb for its runtime services. This will probably
+	 * overlap with our spin table code, which is why we have to relocate
+	 * it.
+	 * Keep this after the __real_cntfrq update, so we have it when we
+	 * copy the complete section here.
+	 */
+	ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
+				 EFI_RESERVED_MEMORY_TYPE,
+				 efi_size_in_pages(secondary_boot_code_size),
+				 &reloc_addr);
+	if (ret == EFI_SUCCESS) {
+		debug("Relocating spin table from %llx to %llx (size %lx)\n",
+		      (u64)secondary_boot_code_start, reloc_addr,
+		      secondary_boot_code_size);
+		memcpy((void *)reloc_addr, secondary_boot_code_start,
+		       secondary_boot_code_size);
+		flush_dcache_range(reloc_addr,
+				   reloc_addr + secondary_boot_code_size);
+
+		/* set new entry point for secondary cores */
+		secondary_boot_addr += (void *)reloc_addr -
+				       secondary_boot_code_start;
+		flush_dcache_range((unsigned long)&secondary_boot_addr,
+				   (unsigned long)&secondary_boot_addr + 8);
+
+		/* this will be used to reserve the memory */
+		secondary_boot_code_start = (void *)reloc_addr;
+	}
+#endif
+
 	cores = cpu_mask();
 	/* Clear spin table so that secondary processors
 	 * observe the correct value after waking up from wfe.