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/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
+}