@@ -48,12 +48,77 @@ pub const SCRATCH_TOP_EXN_STACK_OFFSET: u64 = 0x20;
4848#[ cfg( feature = "nanvix-unstable" ) ]
4949pub const SCRATCH_TOP_GUEST_COUNTER_OFFSET : u64 = 0x1008 ;
5050
51+ /// GPA of the LAPIC access page that KVM reserves when hw-interrupts
52+ /// are enabled. Only relevant for i686 guests in practice,
53+ /// as amd64/aarch64 MAX_GPA is large enough that scratch
54+ /// never reaches this address.
55+ const KVM_APIC_PAGE_GPA : u64 = 0xFEE0_0000 ;
56+
5157pub fn scratch_base_gpa ( size : usize ) -> u64 {
52- ( MAX_GPA - size + 1 ) as u64
58+ let base = ( MAX_GPA - size + 1 ) as u64 ;
59+ if base <= KVM_APIC_PAGE_GPA {
60+ // Scratch would overlap the LAPIC access page at 0xFEE00000.
61+ // Place the entire region just below the APIC page.
62+ KVM_APIC_PAGE_GPA - size as u64
63+ } else {
64+ base
65+ }
5366}
5467pub fn scratch_base_gva ( size : usize ) -> u64 {
5568 ( MAX_GVA - size + 1 ) as u64
5669}
5770
5871/// Compute the minimum scratch region size needed for a sandbox.
5972pub use arch:: min_scratch_size;
73+
74+ #[ cfg( test) ]
75+ mod tests {
76+ use super :: * ;
77+
78+ #[ test]
79+ fn small_scratch_not_relocated ( ) {
80+ // Default scratch (288KB) should not be relocated on any arch.
81+ let size = 0x48000 ;
82+ let base = scratch_base_gpa ( size) ;
83+ assert_eq ! ( base, ( MAX_GPA - size + 1 ) as u64 ) ;
84+ }
85+
86+ #[ test]
87+ fn scratch_avoids_apic_page ( ) {
88+ // For any scratch size, the region [base, base+size) must not
89+ // contain the APIC page at 0xFEE00000.
90+ for & size in & [ 0x48000 , 0x100000 , 0x2000000 , 0x6000000 ] {
91+ let base = scratch_base_gpa ( size) ;
92+ let end = base + size as u64 ;
93+ let apic_start = KVM_APIC_PAGE_GPA ;
94+ let apic_end = KVM_APIC_PAGE_GPA + 0x1000 ;
95+ let overlaps = base < apic_end && end > apic_start;
96+ assert ! (
97+ !overlaps,
98+ "scratch [base={:#x}, end={:#x}) overlaps APIC [{:#x}, {:#x}) for size={:#x}" ,
99+ base, end, apic_start, apic_end, size,
100+ ) ;
101+ }
102+ }
103+
104+ /// On i686, MAX_GPA is 0xFFFFFFFF, so large scratch regions would
105+ /// normally overlap the APIC page. Verify relocation works.
106+ #[ cfg( feature = "nanvix-unstable" ) ]
107+ #[ test]
108+ fn i686_large_scratch_relocated ( ) {
109+ let size = 0x2000000 ; // 32MB
110+ let base = scratch_base_gpa ( size) ;
111+ assert_eq ! ( base, KVM_APIC_PAGE_GPA - size as u64 ) ;
112+ assert ! ( base + size as u64 <= KVM_APIC_PAGE_GPA ) ;
113+ }
114+
115+ #[ cfg( feature = "nanvix-unstable" ) ]
116+ #[ test]
117+ fn i686_scratch_just_fitting_not_relocated ( ) {
118+ // Scratch of ~18MB starts at 0xFEE00001, just above the APIC page.
119+ let size = 0x11FFFFF ;
120+ let base = scratch_base_gpa ( size) ;
121+ assert_eq ! ( base, ( MAX_GPA - size + 1 ) as u64 ) ;
122+ assert ! ( base > KVM_APIC_PAGE_GPA ) ;
123+ }
124+ }
0 commit comments