diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 87a822e5c..724bcfd31 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -86,6 +86,21 @@ SYM_CODE_START(primary_entry)
bl record_mmu_state
bl preserve_boot_args
+ /*
+ * ALARM boot marker, stage 1: primary_entry reached.
+ *
+ * Writes a word to 0xB0000000, the region hekate reserves and preserves
+ * across boots as its pstore carveout (bdk/memory_map.h: PSTORE_ADDR).
+ * The MMU is still off here, so this is a plain physical write. U-Boot
+ * dumps that region to the SD on the next boot, which is the only way to
+ * observe how far the kernel gets when it dies before its first printk.
+ *
+ * x0-x3 are preserved by preserve_boot_args above; x9/x10 are free.
+ */
+ movz x9, #0xB000, lsl #16 // 0xB0000000
+ mov w10, #0xA1
+ str w10, [x9]
+
adrp x1, early_init_stack
mov sp, x1
mov x29, xzr
@@ -127,6 +142,12 @@ SYM_CODE_START(primary_entry)
* the TCR will have been set.
*/
bl __cpu_setup // initialise processor
+
+ /* ALARM boot marker, stage 2: page tables built, CPU setup done, MMU off. */
+ movz x9, #0xB000, lsl #16 // 0xB0000000
+ mov w10, #0xA2
+ str w10, [x9, #4]
+
b __primary_switch
SYM_CODE_END(primary_entry)
diff --git a/init/main.c b/init/main.c
index e363232b4..3a5644a71 100644
--- a/init/main.c
+++ b/init/main.c
@@ -40,6 +40,8 @@
#include <linux/vmalloc.h>
#include <linux/kernel_stat.h>
#include <linux/start_kernel.h>
+/* ALARM: __va() for the boot markers written into hekate's pstore region. */
+#include <asm/memory.h>
#include <linux/security.h>
#include <linux/smp.h>
#include <linux/profile.h>
@@ -991,7 +993,23 @@ void start_kernel(void)
boot_cpu_init();
page_address_init();
pr_notice("%s", linux_banner);
+
+ /*
+ * ALARM boot marker: 0xB0000000 is the region hekate reserves and preserves
+ * across boots as its pstore carveout (bdk/memory_map.h: PSTORE_ADDR).
+ * U-Boot dumps it to the SD on the next boot, which is the only way to see
+ * how far the kernel gets -- the display is tiled and unreadable, there is
+ * no UART, and the kernel dies before its first printk.
+ *
+ * Stages 1 and 2 are written from head.S before the MMU is on (plain
+ * physical writes). From here the linear mapping exists, hence __va().
+ */
+#define ALARM_MARK(n) \
+ (*(volatile u32 *)__va(0xB0000000UL + (n) * 4) = 0xA0A00000u | (n))
+
setup_arch(&command_line);
+ ALARM_MARK(3); /* device tree parsed, memory and paging set up */
+
mm_core_init_early();
/* Static keys and static calls are needed by LSMs */
jump_label_init();
@@ -1086,6 +1104,7 @@ void start_kernel(void)
vdso_setup_data_pages();
timekeeping_init();
time_init();
+ ALARM_MARK(4); /* arch timer initialised */
/* This must be after timekeeping is initialized */
random_init();