diff mbox

[v3] arm64: enforce x1|x2|x3 == 0 upon kernel entry as per boot protocol

Message ID 1426762852-13699-1-git-send-email-ard.biesheuvel@linaro.org
State New
Headers show

Commit Message

Ard Biesheuvel March 19, 2015, 11 a.m. UTC
According to the arm64 boot protocol, registers x1 to x3 should be
zero upon kernel entry, and non-zero values are reserved for future
use. This future use is going to be problematic if we never enforce
the current rules, so start enforcing them now, by emitting a warning
if non-zero values are detected.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 arch/arm64/kernel/head.S  | 19 ++++++++++++++++++-
 arch/arm64/kernel/setup.c | 10 ++++++++++
 2 files changed, 28 insertions(+), 1 deletion(-)

Comments

Ard Biesheuvel March 20, 2015, 11:31 a.m. UTC | #1
On 19 March 2015 at 14:36, Mark Rutland <mark.rutland@arm.com> wrote:
> On Thu, Mar 19, 2015 at 11:00:52AM +0000, Ard Biesheuvel wrote:
>> According to the arm64 boot protocol, registers x1 to x3 should be
>> zero upon kernel entry, and non-zero values are reserved for future
>> use. This future use is going to be problematic if we never enforce
>> the current rules, so start enforcing them now, by emitting a warning
>> if non-zero values are detected.
>>
>> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>> ---
>>  arch/arm64/kernel/head.S  | 19 ++++++++++++++++++-
>>  arch/arm64/kernel/setup.c | 10 ++++++++++
>>  2 files changed, 28 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
>> index f5ac337f9598..1fdf42041f42 100644
>> --- a/arch/arm64/kernel/head.S
>> +++ b/arch/arm64/kernel/head.S
>> @@ -233,7 +233,7 @@ section_table:
>>  #endif
>>
>>  ENTRY(stext)
>> -     mov     x21, x0                         // x21=FDT
>> +     bl      preserve_boot_args
>>       bl      el2_setup                       // Drop to EL1, w20=cpu_boot_mode
>>       adrp    x24, __PHYS_OFFSET
>>       bl      set_cpu_boot_mode_flag
>> @@ -253,6 +253,23 @@ ENTRY(stext)
>>  ENDPROC(stext)
>>
>>  /*
>> + * Preserve the arguments passed by the bootloader in x0 .. x3
>> + */
>> +preserve_boot_args:
>> +     mov     x21, x0                         // x21=FDT
>> +
>> +     adr_l   x0, boot_args                   // record the contents of
>> +     stp     x21, x1, [x0]                   // x0 .. x3 at kernel entry
>> +     stp     x2, x3, [x0, #16]
>> +
>> +     dmb     sy                              // needed before dc ivac with
>> +                                             // MMU off
>> +
>> +     add     x1, x0, #0x20                   // 4 x 8 bytes
>> +     b       __inval_cache_range             // tail call
>> +ENDPROC(preserve_boot_args)
>> +
>> +/*
>>   * Determine validity of the x21 FDT pointer.
>>   * The dtb must be 8-byte aligned and live in the first 512M of memory.
>>   */
>> diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
>> index 1783b38cf4c0..2f384019b201 100644
>> --- a/arch/arm64/kernel/setup.c
>> +++ b/arch/arm64/kernel/setup.c
>> @@ -115,6 +115,11 @@ void __init early_print(const char *str, ...)
>>       printk("%s", buf);
>>  }
>>
>> +/*
>> + * The recorded values of x0 .. x3 upon kernel entry.
>> + */
>> +u64 __cacheline_aligned boot_args[4];
>
> All the above looks correct to me.
>
>> +
>>  void __init smp_setup_processor_id(void)
>>  {
>>       u64 mpidr = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
>> @@ -412,6 +417,11 @@ void __init setup_arch(char **cmdline_p)
>>       conswitchp = &dummy_con;
>>  #endif
>>  #endif
>> +     if (boot_args[1] || boot_args[2] || boot_args[3]) {
>> +             pr_err("WARNING: boot protocol violation detected (x1 == %llx, x2 == %llx, x3 == %llx)\n",
>> +                     boot_args[1], boot_args[2], boot_args[3]);
>> +             pr_err("WARNING: your bootloader may fail to load newer kernels\n");
>
> If we ever decide to use x1-x3 for something, and try to boot an older
> kernel, that warning is going to be a bit misleading. That could matter
> for VMs where we're going to see old kernel images for a long time.
>
> I would like the warning to mention that could be the case.
>
> It would also be nice if the message were consistently spaced regardless
> of the values of x1-x3, so we should zero-pad them (and as that takes a
> resonable amount of space, let's give them a line each).
>
> So could we change the warning to be something like:
>
>         pr_err("WARNING: x1-x3 nonzero in violation of boot protocol:\n"
>                "\tx1: %016llx\n\tx2: %016llx\n\tx3: %016llx\n"
>                "This indicates a broken bootloader or old kernel\n",
>                boot_args[1], boot_args[2], boot_args[3]);
>

OK, I have applied this change.

But I would like to note that we should probably only extend the boot
protocol in a way that would not trigger this on older kernels in the
first place.
I.e., assign a bit in the flags field in the header, which indicates
whether some boot protocol enhancement is supported by the kernel
being loaded, and only allow x1/x2/x3 to be non-zero if said
enhancement defines that.
Ard Biesheuvel March 20, 2015, 11:45 a.m. UTC | #2
On 20 March 2015 at 12:41, Mark Rutland <mark.rutland@arm.com> wrote:
>> >> +     if (boot_args[1] || boot_args[2] || boot_args[3]) {
>> >> +             pr_err("WARNING: boot protocol violation detected (x1 == %llx, x2 == %llx, x3 == %llx)\n",
>> >> +                     boot_args[1], boot_args[2], boot_args[3]);
>> >> +             pr_err("WARNING: your bootloader may fail to load newer kernels\n");
>> >
>> > If we ever decide to use x1-x3 for something, and try to boot an older
>> > kernel, that warning is going to be a bit misleading. That could matter
>> > for VMs where we're going to see old kernel images for a long time.
>> >
>> > I would like the warning to mention that could be the case.
>> >
>> > It would also be nice if the message were consistently spaced regardless
>> > of the values of x1-x3, so we should zero-pad them (and as that takes a
>> > resonable amount of space, let's give them a line each).
>> >
>> > So could we change the warning to be something like:
>> >
>> >         pr_err("WARNING: x1-x3 nonzero in violation of boot protocol:\n"
>> >                "\tx1: %016llx\n\tx2: %016llx\n\tx3: %016llx\n"
>> >                "This indicates a broken bootloader or old kernel\n",
>> >                boot_args[1], boot_args[2], boot_args[3]);
>> >
>>
>> OK, I have applied this change.
>>
>> But I would like to note that we should probably only extend the boot
>> protocol in a way that would not trigger this on older kernels in the
>> first place.
>> I.e., assign a bit in the flags field in the header, which indicates
>> whether some boot protocol enhancement is supported by the kernel
>> being loaded, and only allow x1/x2/x3 to be non-zero if said
>> enhancement defines that.
>
> Good point.
>
> Given that, if you want to restore your original last line, that would
> be fine with me (and my Ack still applies).
>

I think it's fine to leave it as is
Ard Biesheuvel March 20, 2015, 12:50 p.m. UTC | #3
On 20 March 2015 at 13:25, Will Deacon <will.deacon@arm.com> wrote:
> On Fri, Mar 20, 2015 at 11:45:17AM +0000, Ard Biesheuvel wrote:
>> On 20 March 2015 at 12:41, Mark Rutland <mark.rutland@arm.com> wrote:
>> >> >> +     if (boot_args[1] || boot_args[2] || boot_args[3]) {
>> >> >> +             pr_err("WARNING: boot protocol violation detected (x1 == %llx, x2 == %llx, x3 == %llx)\n",
>> >> >> +                     boot_args[1], boot_args[2], boot_args[3]);
>> >> >> +             pr_err("WARNING: your bootloader may fail to load newer kernels\n");
>> >> >
>> >> > If we ever decide to use x1-x3 for something, and try to boot an older
>> >> > kernel, that warning is going to be a bit misleading. That could matter
>> >> > for VMs where we're going to see old kernel images for a long time.
>> >> >
>> >> > I would like the warning to mention that could be the case.
>> >> >
>> >> > It would also be nice if the message were consistently spaced regardless
>> >> > of the values of x1-x3, so we should zero-pad them (and as that takes a
>> >> > resonable amount of space, let's give them a line each).
>> >> >
>> >> > So could we change the warning to be something like:
>> >> >
>> >> >         pr_err("WARNING: x1-x3 nonzero in violation of boot protocol:\n"
>> >> >                "\tx1: %016llx\n\tx2: %016llx\n\tx3: %016llx\n"
>> >> >                "This indicates a broken bootloader or old kernel\n",
>> >> >                boot_args[1], boot_args[2], boot_args[3]);
>> >> >
>> >>
>> >> OK, I have applied this change.
>> >>
>> >> But I would like to note that we should probably only extend the boot
>> >> protocol in a way that would not trigger this on older kernels in the
>> >> first place.
>> >> I.e., assign a bit in the flags field in the header, which indicates
>> >> whether some boot protocol enhancement is supported by the kernel
>> >> being loaded, and only allow x1/x2/x3 to be non-zero if said
>> >> enhancement defines that.
>> >
>> > Good point.
>> >
>> > Given that, if you want to restore your original last line, that would
>> > be fine with me (and my Ack still applies).
>> >
>>
>> I think it's fine to leave it as is
>
> Yup, and this is sitting pretty on the arm64 devel branch.
>
> Ard: I also pushed a kvm-bounce-page branch for you. Next step would be to
> merge everything into for-next/core and put your VA changes on top of that.
>
> I'd appreciate a sanity check of the current branch first, though!
>

OK, I pulled devel and put the KVM and VA branches on top, and
everything builds and runs fine afaict.

I also checked that Arnd's dotconfig from hell does not have the false
negative on the HYP text size assert.

"""
0x00000001                ASSERT ((((__hyp_idmap_text_start & 0xfff) +
__hyp_idmap_size) <= 0x1000), , HYP init code too big or misaligned)
"""

(Note that this .config does not even produce a bootable zImage.)

You will get a conflict in head.S when you merge the core VA range patch.
Nothing major but I'm happy to resend the patch.
diff mbox

Patch

diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index f5ac337f9598..1fdf42041f42 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -233,7 +233,7 @@  section_table:
 #endif
 
 ENTRY(stext)
-	mov	x21, x0				// x21=FDT
+	bl	preserve_boot_args
 	bl	el2_setup			// Drop to EL1, w20=cpu_boot_mode
 	adrp	x24, __PHYS_OFFSET
 	bl	set_cpu_boot_mode_flag
@@ -253,6 +253,23 @@  ENTRY(stext)
 ENDPROC(stext)
 
 /*
+ * Preserve the arguments passed by the bootloader in x0 .. x3
+ */
+preserve_boot_args:
+	mov	x21, x0				// x21=FDT
+
+	adr_l	x0, boot_args			// record the contents of
+	stp	x21, x1, [x0]			// x0 .. x3 at kernel entry
+	stp	x2, x3, [x0, #16]
+
+	dmb	sy				// needed before dc ivac with
+						// MMU off
+
+	add	x1, x0, #0x20			// 4 x 8 bytes
+	b	__inval_cache_range		// tail call
+ENDPROC(preserve_boot_args)
+
+/*
  * Determine validity of the x21 FDT pointer.
  * The dtb must be 8-byte aligned and live in the first 512M of memory.
  */
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 1783b38cf4c0..2f384019b201 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -115,6 +115,11 @@  void __init early_print(const char *str, ...)
 	printk("%s", buf);
 }
 
+/*
+ * The recorded values of x0 .. x3 upon kernel entry.
+ */
+u64 __cacheline_aligned boot_args[4];
+
 void __init smp_setup_processor_id(void)
 {
 	u64 mpidr = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
@@ -412,6 +417,11 @@  void __init setup_arch(char **cmdline_p)
 	conswitchp = &dummy_con;
 #endif
 #endif
+	if (boot_args[1] || boot_args[2] || boot_args[3]) {
+		pr_err("WARNING: boot protocol violation detected (x1 == %llx, x2 == %llx, x3 == %llx)\n",
+			boot_args[1], boot_args[2], boot_args[3]);
+		pr_err("WARNING: your bootloader may fail to load newer kernels\n");
+	}
 }
 
 static int __init arm64_device_init(void)