vmbase: Handle stack overflows

Allocate the first page of the writable_data region for a stack to be
used by the exception handlers so that faults caused by accesses to the
SP from the main thread can be handled. As fault handlers push the
register file to the stack on entry, we would previously enter an
exception loop on stack overflow.

This works by reserving SP_EL0 for the "main" thread and relying on
current_exception_sp0 (from exceptions.S) switching to SP_EL1 when
taking an exception. SP_EL1 is first initialized to the bottom of the
newly allocated page while SP_EL0 is initialized to the value previously
used (note that SP_EL0 was previously unused).

Use the linker script to ensure that there is always at least one full
page between the end of .bss and the top (i.e. smallest address) of the
stack, in order to guarantee a permission fault on stack overflow, if
the MMU has been set properly.

Rely on the fact that the page preceding the page containing the EH
stack isn't mapped R/W to catch EH stack overflows (which would still
end up in an exception loop).

Bug: 279209532
Test: atest MicrodroidTests
Change-Id: Ie5a8dc06348bfb7db2742b1affec4d162d8b538c
diff --git a/vmbase/entry.S b/vmbase/entry.S
index 177c9aa..9f6993a 100644
--- a/vmbase/entry.S
+++ b/vmbase/entry.S
@@ -208,8 +208,14 @@
 	stp q0, q1, [x28], #32
 	b 2b
 
-3:	/* Prepare the stack. */
+3:	/* Prepare the exception handler stack (SP_EL1). */
+	adr_l x30, init_eh_stack_pointer
+	msr spsel, #1
+	mov sp, x30
+
+	/* Prepare the main thread stack (SP_EL0). */
 	adr_l x30, init_stack_pointer
+	msr spsel, #0
 	mov sp, x30
 
 	/* Set up exception vector. */
diff --git a/vmbase/sections.ld b/vmbase/sections.ld
index 8118363..5232d30 100644
--- a/vmbase/sections.ld
+++ b/vmbase/sections.ld
@@ -60,6 +60,17 @@
 	} >image
 	rodata_end = .;
 
+	.eh_stack (NOLOAD) : ALIGN(4096) {
+		/*
+		 * Get stack overflow guard from the previous page being from
+		 * .rodata and mapped read-only or left unmapped.
+		 */
+		eh_stack_limit = .;
+		. += 4096;
+		. = ALIGN(4096);
+		init_eh_stack_pointer = .;
+	} >writable_data
+
 	/*
 	 * Collect together the read-write data including .bss at the end which
 	 * will be zero'd by the entry code. This is page aligned so it can be
@@ -91,6 +102,7 @@
 
 	init_stack_pointer = ORIGIN(writable_data) + LENGTH(writable_data);
 	.stack (NOLOAD) : ALIGN(4096) {
+		. += 4096; /* Ensure we have one guard page for overflow. */
 		stack_limit = .;
 		. = init_stack_pointer;
 	} >writable_data
diff --git a/vmbase/src/layout.rs b/vmbase/src/layout.rs
index d07dac4..ead4f8e 100644
--- a/vmbase/src/layout.rs
+++ b/vmbase/src/layout.rs
@@ -72,7 +72,7 @@
 
 /// All writable sections, excluding the stack.
 pub fn scratch_range() -> Range<usize> {
-    linker_region!(data_begin, bss_end)
+    linker_region!(eh_stack_limit, bss_end)
 }
 
 /// Read-write data (original).
diff --git a/vmbase/src/linker.rs b/vmbase/src/linker.rs
index 5f6e05e..97bef3f 100644
--- a/vmbase/src/linker.rs
+++ b/vmbase/src/linker.rs
@@ -33,6 +33,8 @@
     pub static dtb_begin: u8;
     /// First byte beyond the `.dtb` section.
     pub static dtb_end: u8;
+    /// First byte of the region available for the exception handler stack.
+    pub static eh_stack_limit: u8;
     /// First byte past the region available for the stack.
     pub static init_stack_pointer: u8;
     /// First byte of the `.rodata` section.