vmbase: Default to largest stack size possible

Remove the need for clients to decide how many pages the stack should
use, which was originally added to pvmfw to reduce its boot times (as it
must zero its stack) but was ported to the other clients, where it does
not make sense. Instead, map all the available pages at the bottom of
the writable_data region by default. Note that the linker script still
reserves one page that remains unmapped, to catch stack overflows.

For pvmfw, provide a macro to define its desired stack size.

As a result, vmbase can expose a layout::stack_range() without argument
that matches the client. This will enable moving dynamic PT creation
into the library, which is needed to enter clients with the dynamic PTs
enabled.

As I wasn't sure about the linker trick being used, this change was
validated by checking the disassembled clients:

pvmfw:

  000000007fc1e454 <pvmfw::entry::main_wrapper>:
      [...]
      7fc1e5bc:   94005422        bl      7fc33644 <vmbase_stack_limit_client>
      [...]
  000000007fc33644 <vmbase_stack_limit_client>:
      7fc33644:   a9bf7bfd        stp     x29, x30, [sp, #-16]!
      7fc33648:   910003fd        mov     x29, sp
      7fc3364c:   52800020        mov     w0, #0x1                        // #1
      7fc33650:   52980001        mov     w1, #0xc000                     // #49152
      7fc33654:   a8c17bfd        ldp     x29, x30, [sp], #16
      7fc33658:   d65f03c0        ret
      [...]
  000000007fc3be48 <vmbase::layout::stack_range>:
      [...]
      7fc3be64:   97ffddf8        bl      7fc33644 <vmbase_stack_limit_client>

Rialto:

  0000000080012e20 <rialto::main>:
      [...]
      80013028:   940028e7        bl      8001d3c4 <vmbase_stack_limit_default>
      [...]
  000000008001d3c4 <vmbase_stack_limit_default>:
      8001d3c4:   a9bf7bfd        stp     x29, x30, [sp, #-16]!
      8001d3c8:   910003fd        mov     x29, sp
      8001d3cc:   aa1f03e0        mov     x0, xzr
      8001d3d0:   a8c17bfd        ldp     x29, x30, [sp], #16
      8001d3d4:   d65f03c0        ret

vmbase_example:

  000000008020861c <vmbase_example::main>:
      [...]
      80208e48:   94002b7e        bl      80213c40 <vmbase_stack_limit_default>
      [...]
      80209268:   94002a76        bl      80213c40 <vmbase_stack_limit_default>
  000000008001d3c4 <vmbase_stack_limit_default>:
      [...]
      80213c50:   d65f03c0        ret

which all WAI.

Bug: 377276983
Test: m {pvmfw,rialto,vmbase_example_{bios,kernel}}_bin
Test: atest rialto_test vmbase_example.integration_test
Change-Id: I8df6762954b3633b737d75f6221a75b8f9bea015
diff --git a/guest/pvmfw/src/entry.rs b/guest/pvmfw/src/entry.rs
index 0607a5a..1da0513 100644
--- a/guest/pvmfw/src/entry.rs
+++ b/guest/pvmfw/src/entry.rs
@@ -26,7 +26,7 @@
 use vmbase::util::RangeExt as _;
 use vmbase::{
     arch::aarch64::min_dcache_line_size,
-    configure_heap, console_writeln, layout, main,
+    configure_heap, console_writeln, layout, limit_stack_size, main,
     memory::{
         deactivate_dynamic_page_tables, map_image_footer, switch_to_dynamic_page_tables,
         unshare_all_memory, unshare_all_mmio_except_uart, unshare_uart, MemoryTrackerError,
@@ -73,6 +73,7 @@
 
 main!(start);
 configure_heap!(SIZE_128KB);
+limit_stack_size!(SIZE_4KB * 12);
 
 /// Entry point for pVM firmware.
 pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
@@ -190,7 +191,7 @@
     assert_eq!(bcc.start % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
     assert_eq!(bcc.end % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
 
-    let stack = memory::stack_range();
+    let stack = layout::stack_range();
 
     assert_ne!(stack.end - stack.start, 0, "stack region is empty.");
     assert_eq!(stack.start.0 % ASM_STP_ALIGN, 0, "Misaligned stack region.");
diff --git a/guest/pvmfw/src/helpers.rs b/guest/pvmfw/src/helpers.rs
index 8981408..0552640 100644
--- a/guest/pvmfw/src/helpers.rs
+++ b/guest/pvmfw/src/helpers.rs
@@ -14,7 +14,6 @@
 
 //! Miscellaneous helper functions.
 
-use vmbase::memory::{PAGE_SIZE, SIZE_4KB};
+use vmbase::memory::SIZE_4KB;
 
 pub const GUEST_PAGE_SIZE: usize = SIZE_4KB;
-pub const PVMFW_PAGE_SIZE: usize = PAGE_SIZE;
diff --git a/guest/pvmfw/src/memory.rs b/guest/pvmfw/src/memory.rs
index b54f014..a0d2b14 100644
--- a/guest/pvmfw/src/memory.rs
+++ b/guest/pvmfw/src/memory.rs
@@ -16,11 +16,8 @@
 
 use crate::entry::RebootReason;
 use crate::fdt;
-use crate::helpers::PVMFW_PAGE_SIZE;
-use aarch64_paging::paging::VirtualAddress;
 use aarch64_paging::MapError;
 use core::num::NonZeroUsize;
-use core::ops::Range;
 use core::result;
 use core::slice;
 use log::debug;
@@ -32,13 +29,6 @@
     memory::{init_shared_pool, map_data, map_rodata, resize_available_memory, PageTable},
 };
 
-/// Region allocated for the stack.
-pub fn stack_range() -> Range<VirtualAddress> {
-    const STACK_PAGES: usize = 12;
-
-    layout::stack_range(STACK_PAGES * PVMFW_PAGE_SIZE)
-}
-
 pub fn init_page_table() -> result::Result<PageTable, MapError> {
     let mut page_table = PageTable::default();
 
@@ -46,7 +36,7 @@
     // so dirty state management can be omitted.
     page_table.map_data(&layout::data_bss_range().into())?;
     page_table.map_data(&layout::eh_stack_range().into())?;
-    page_table.map_data(&stack_range().into())?;
+    page_table.map_data(&layout::stack_range().into())?;
     page_table.map_code(&layout::text_range().into())?;
     page_table.map_rodata(&layout::rodata_range().into())?;
     if let Err(e) = page_table.map_device(&layout::console_uart_page().into()) {
diff --git a/guest/rialto/src/main.rs b/guest/rialto/src/main.rs
index 8095a1f..8f5a670 100644
--- a/guest/rialto/src/main.rs
+++ b/guest/rialto/src/main.rs
@@ -50,7 +50,7 @@
     main,
     memory::{
         init_shared_pool, map_rodata, map_rodata_outside_main_memory, resize_available_memory,
-        switch_to_dynamic_page_tables, PageTable, PAGE_SIZE, SIZE_128KB,
+        switch_to_dynamic_page_tables, PageTable, SIZE_128KB,
     },
     power::reboot,
     virtio::{
@@ -76,7 +76,7 @@
 
     page_table.map_data(&layout::data_bss_range().into())?;
     page_table.map_data(&layout::eh_stack_range().into())?;
-    page_table.map_data(&layout::stack_range(40 * PAGE_SIZE).into())?;
+    page_table.map_data(&layout::stack_range().into())?;
     page_table.map_code(&layout::text_range().into())?;
     page_table.map_rodata(&layout::rodata_range().into())?;
     page_table.map_device(&layout::console_uart_page().into())?;
diff --git a/guest/vmbase_example/src/layout.rs b/guest/vmbase_example/src/layout.rs
index 4e87e4e..bafce10 100644
--- a/guest/vmbase_example/src/layout.rs
+++ b/guest/vmbase_example/src/layout.rs
@@ -14,15 +14,8 @@
 
 //! Memory layout.
 
-use aarch64_paging::paging::VirtualAddress;
-use core::ops::Range;
 use log::info;
-use vmbase::{layout, memory::PAGE_SIZE};
-
-/// Writable data region for the stack.
-pub fn boot_stack_range() -> Range<VirtualAddress> {
-    layout::stack_range(40 * PAGE_SIZE)
-}
+use vmbase::layout;
 
 pub fn print_addresses() {
     let text = layout::text_range();
@@ -40,7 +33,7 @@
     );
     let bss = layout::bss_range();
     info!("bss:        {}..{} ({} bytes)", bss.start, bss.end, bss.end - bss.start);
-    let boot_stack = boot_stack_range();
+    let boot_stack = layout::stack_range();
     info!(
         "boot_stack: {}..{} ({} bytes)",
         boot_stack.start,
diff --git a/guest/vmbase_example/src/main.rs b/guest/vmbase_example/src/main.rs
index 3bcc072..138c90e 100644
--- a/guest/vmbase_example/src/main.rs
+++ b/guest/vmbase_example/src/main.rs
@@ -23,7 +23,7 @@
 
 extern crate alloc;
 
-use crate::layout::{boot_stack_range, print_addresses};
+use crate::layout::print_addresses;
 use crate::pci::check_pci;
 use aarch64_paging::MapError;
 use alloc::{vec, vec::Vec};
@@ -37,7 +37,7 @@
     generate_image_header,
     layout::{
         console_uart_page, crosvm::FDT_MAX_SIZE, data_bss_range, eh_stack_range, rodata_range,
-        text_range,
+        stack_range, text_range,
     },
     linker, logger, main,
     memory::{
@@ -60,7 +60,7 @@
     page_table.map_rodata(&rodata_range().into())?;
     page_table.map_data(&data_bss_range().into())?;
     page_table.map_data(&eh_stack_range().into())?;
-    page_table.map_data(&boot_stack_range().into())?;
+    page_table.map_data(&stack_range().into())?;
 
     Ok(())
 }
diff --git a/libs/libvmbase/sections.ld b/libs/libvmbase/sections.ld
index 222edae..9d69935 100644
--- a/libs/libvmbase/sections.ld
+++ b/libs/libvmbase/sections.ld
@@ -132,3 +132,10 @@
 		*(.note.gnu.build-id)
 	}
 }
+
+/*
+ * Make calling the limit_stack_size!() macro optional by providing a default.
+ */
+PROVIDE(vmbase_stack_limit = DEFINED(vmbase_stack_limit_client) ?
+                                     vmbase_stack_limit_client :
+                                     vmbase_stack_limit_default);
diff --git a/libs/libvmbase/src/layout.rs b/libs/libvmbase/src/layout.rs
index a8f7827..9a702b0 100644
--- a/libs/libvmbase/src/layout.rs
+++ b/libs/libvmbase/src/layout.rs
@@ -17,7 +17,7 @@
 pub mod crosvm;
 
 use crate::linker::__stack_chk_guard;
-use crate::memory::{page_4kb_of, PAGE_SIZE};
+use crate::memory::{max_stack_size, page_4kb_of, PAGE_SIZE};
 use aarch64_paging::paging::VirtualAddress;
 use core::ops::Range;
 use core::ptr::addr_of;
@@ -91,10 +91,16 @@
 }
 
 /// Writable data region for the stack.
-pub fn stack_range(stack_size: usize) -> Range<VirtualAddress> {
+pub fn stack_range() -> Range<VirtualAddress> {
     let end = linker_addr!(init_stack_pointer);
-    let start = VirtualAddress(end.0.checked_sub(stack_size).unwrap());
-    assert!(start >= linker_addr!(stack_limit));
+    let start = if let Some(stack_size) = max_stack_size() {
+        assert_eq!(stack_size % PAGE_SIZE, 0);
+        let start = VirtualAddress(end.0.checked_sub(stack_size).unwrap());
+        assert!(start >= linker_addr!(stack_limit));
+        start
+    } else {
+        linker_addr!(stack_limit)
+    };
 
     start..end
 }
diff --git a/libs/libvmbase/src/memory.rs b/libs/libvmbase/src/memory.rs
index 145f766..e903858 100644
--- a/libs/libvmbase/src/memory.rs
+++ b/libs/libvmbase/src/memory.rs
@@ -18,6 +18,7 @@
 mod error;
 mod page_table;
 mod shared;
+mod stack;
 mod tracker;
 mod util;
 
@@ -35,5 +36,6 @@
 };
 
 pub(crate) use shared::{alloc_shared, dealloc_shared};
+pub(crate) use stack::max_stack_size;
 pub(crate) use tracker::MEMORY;
 pub(crate) use util::{phys_to_virt, virt_to_phys};
diff --git a/libs/libvmbase/src/memory/stack.rs b/libs/libvmbase/src/memory/stack.rs
new file mode 100644
index 0000000..639029e
--- /dev/null
+++ b/libs/libvmbase/src/memory/stack.rs
@@ -0,0 +1,41 @@
+// Copyright 2024, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Low-level stack support.
+
+/// Configures the maximum size of the stack.
+#[macro_export]
+macro_rules! limit_stack_size {
+    ($len:expr) => {
+        #[export_name = "vmbase_stack_limit_client"]
+        fn __vmbase_stack_limit_client() -> Option<usize> {
+            Some($len)
+        }
+    };
+}
+
+pub(crate) fn max_stack_size() -> Option<usize> {
+    extern "Rust" {
+        fn vmbase_stack_limit() -> Option<usize>;
+    }
+    // SAFETY: This function is safe to call as the linker script aliases it to either:
+    // - the safe vmbase_stack_limit_default();
+    // - the safe vmbase_stack_limit_client() potentially defined using limit_stack_size!()
+    unsafe { vmbase_stack_limit() }
+}
+
+#[no_mangle]
+fn vmbase_stack_limit_default() -> Option<usize> {
+    None
+}