#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
        b       __cpu_setup                     // initialise processor
 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.
 
        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;
        conswitchp = &dummy_con;
 #endif
 #endif
+       if (boot_args[1] || boot_args[2] || boot_args[3]) {
+               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]);
+       }
 }
 
 static int __init arm64_device_init(void)