@@ -2,11 +2,21 @@ use core::alloc::{AllocError, Allocator, Layout};
22use core:: ptr:: { self , NonNull } ;
33
44use align_address:: Align ;
5+ #[ cfg( target_arch = "x86_64" ) ]
6+ use free_list:: FreeList ;
57use free_list:: { PageLayout , PageRange } ;
8+ #[ cfg( target_arch = "x86_64" ) ]
9+ use hermit_sync:: InterruptTicketMutex ;
610use memory_addresses:: { PhysAddr , VirtAddr } ;
11+ #[ cfg( target_arch = "x86_64" ) ]
12+ use x86_64:: structures:: paging:: PhysFrame ;
713
8- use crate :: arch:: mm:: paging:: { BasePageSize , PageSize } ;
9- use crate :: mm:: { FrameAlloc , PageRangeAllocator , virtualmem} ;
14+ use crate :: arch:: mm:: paging;
15+ use crate :: arch:: mm:: paging:: { BasePageSize , HugePageSize , PageSize } ;
16+ #[ cfg( target_arch = "x86_64" ) ]
17+ use crate :: arch:: mm:: paging:: { LargePageSize , PageTableEntryFlags } ;
18+ use crate :: env;
19+ use crate :: mm:: { FrameAlloc , PageRangeAllocator } ;
1020
1121/// An [`Allocator`] for memory that is used to communicate with devices.
1222///
@@ -19,7 +29,15 @@ unsafe impl Allocator for DeviceAlloc {
1929 let size = layout. size ( ) . align_up ( BasePageSize :: SIZE as usize ) ;
2030 let frame_layout = PageLayout :: from_size ( size) . unwrap ( ) ;
2131
22- let frame_range = FrameAlloc :: allocate ( frame_layout) . map_err ( |_| AllocError ) ?;
32+ let frame_range = if const { DeviceAlloc . phys_offset ( ) . is_null ( ) } {
33+ FrameAlloc :: allocate ( frame_layout)
34+ } else {
35+ cfg_select ! {
36+ target_arch = "x86_64" => DeviceFreeList :: allocate( frame_layout) ,
37+ _ => unreachable!( )
38+ }
39+ }
40+ . map_err ( |_| AllocError ) ?;
2341
2442 let phys_addr = PhysAddr :: from ( frame_range. start ( ) ) ;
2543 let ptr = self . ptr_from ( phys_addr) ;
@@ -34,17 +52,22 @@ unsafe impl Allocator for DeviceAlloc {
3452 let phys_addr = self . phys_addr_from ( ptr. as_ptr ( ) ) ;
3553 let range = PageRange :: from_start_len ( phys_addr. as_usize ( ) , size) . unwrap ( ) ;
3654
37- unsafe {
38- FrameAlloc :: deallocate ( range) ;
39- }
55+ if const { DeviceAlloc . phys_offset ( ) . is_null ( ) } {
56+ unsafe { FrameAlloc :: deallocate ( range) }
57+ } else {
58+ cfg_select ! {
59+ target_arch = "x86_64" => unsafe { DeviceFreeList :: deallocate( range) } ,
60+ _ => unreachable!( )
61+ }
62+ } ;
4063 }
4164}
4265
4366impl DeviceAlloc {
4467 /// Returns a pointer corresponding to `phys_addr`.
4568 #[ inline]
46- pub fn ptr_from < T > ( & self , phys_addr : PhysAddr ) -> * mut T {
47- let addr = phys_addr. as_usize ( ) + self . phys_offset ( ) . as_usize ( ) ;
69+ pub const fn ptr_from < T > ( & self , phys_addr : PhysAddr ) -> * mut T {
70+ let addr = phys_addr. as_usize ( ) + const { Self . phys_offset ( ) . as_usize ( ) } ;
4871 ptr:: with_exposed_provenance_mut ( addr)
4972 }
5073
@@ -53,19 +76,121 @@ impl DeviceAlloc {
5376 /// The address is only correct if `ptr` has been allocated by this allocator.
5477 #[ inline]
5578 pub fn phys_addr_from < T : ?Sized > ( & self , ptr : * mut T ) -> PhysAddr {
56- let addr = u64:: try_from ( ptr. expose_provenance ( ) ) . unwrap ( ) - self . phys_offset ( ) . as_u64 ( ) ;
79+ let addr =
80+ u64:: try_from ( ptr. expose_provenance ( ) ) . unwrap ( ) - const { Self . phys_offset ( ) . as_u64 ( ) } ;
5781 PhysAddr :: new ( addr)
5882 }
5983
6084 /// Returns the physical address offset.
6185 ///
6286 /// This device allocator expects the complete physical memory to be mapped device-readable at this offset.
63- #[ inline]
64- pub fn phys_offset ( & self ) -> VirtAddr {
65- if cfg ! ( careful) {
66- virtualmem:: kernel_heap_end ( ) . as_u64 ( ) . div_ceil ( 4 ) . into ( )
67- } else {
68- 0u64 . into ( )
87+ #[ inline( always) ]
88+ const fn phys_offset ( & self ) -> VirtAddr {
89+ cfg_select ! {
90+ all( target_arch = "x86_64" , careful) => VirtAddr :: new( crate :: mm:: virtualmem:: kernel_heap_end( ) . as_u64( ) . div_ceil( 4 ) ) ,
91+ _ => VirtAddr :: zero( ) ,
92+ }
93+ }
94+
95+ pub fn init ( ) {
96+ // Remove all mappings in the device allocator range
97+ if env:: is_uefi ( ) && DeviceAlloc . phys_offset ( ) != VirtAddr :: zero ( ) {
98+ let start = DeviceAlloc . phys_offset ( ) ;
99+ let count = DeviceAlloc . phys_offset ( ) . as_u64 ( ) / HugePageSize :: SIZE ;
100+ let count = usize:: try_from ( count) . unwrap ( ) ;
101+ paging:: unmap :: < HugePageSize > ( start, count) ;
102+ }
103+ }
104+ }
105+
106+ #[ cfg( target_arch = "x86_64" ) ]
107+ static DEVICE_FREE_LIST : InterruptTicketMutex < FreeList < 16 > > =
108+ InterruptTicketMutex :: new ( FreeList :: new ( ) ) ;
109+
110+ #[ cfg( target_arch = "x86_64" ) ]
111+ struct DeviceFreeList ;
112+
113+ #[ cfg( target_arch = "x86_64" ) ]
114+ type DeviceAllocIncrement = LargePageSize ;
115+
116+ #[ cfg( target_arch = "x86_64" ) ]
117+ impl DeviceFreeList {
118+ fn allocate ( layout : PageLayout ) -> Result < PageRange , AllocError > {
119+ let allocation = DEVICE_FREE_LIST . lock ( ) . allocate ( layout) ;
120+
121+ match allocation {
122+ Err ( _) => {
123+ // Failed allocation: try to claim pages from the main FrameAllocator then retry
124+ let aligned_layout = PageLayout :: from_size_align (
125+ layout. size ( ) . align_up ( DeviceAllocIncrement :: SIZE as usize ) ,
126+ DeviceAllocIncrement :: SIZE as usize ,
127+ )
128+ . unwrap ( ) ;
129+
130+ let frames = FrameAlloc :: allocate ( aligned_layout) ?;
131+ let start = x86_64:: PhysAddr :: new ( u64:: try_from ( frames. start ( ) ) . unwrap ( ) ) ;
132+ let start = PhysFrame :: < DeviceAllocIncrement > :: from_start_address ( start) . unwrap ( ) ;
133+ let end = x86_64:: PhysAddr :: new ( u64:: try_from ( frames. end ( ) ) . unwrap ( ) ) ;
134+ let end = PhysFrame :: < DeviceAllocIncrement > :: from_start_address ( end) . unwrap ( ) ;
135+
136+ for frame in PhysFrame :: range ( start, end) {
137+ unsafe {
138+ Self :: map_claim_frame ( frame) ?;
139+ }
140+ }
141+
142+ // Retry allocation
143+ DEVICE_FREE_LIST
144+ . lock ( )
145+ . allocate ( layout)
146+ . map_err ( |_| AllocError )
147+ }
148+ Ok ( r) => Ok ( r) ,
149+ }
150+ }
151+
152+ /// # Safety
153+ ///
154+ /// See [PageRangeAllocator::deallocate]
155+ unsafe fn deallocate ( range : PageRange ) {
156+ // OPTIONAL: if we have too much memory in the list we may return it to the physical free list
157+ // In this case, we MUST unmap it/remap it as identity
158+ unsafe {
159+ // SAFETY: invariants match
160+ DEVICE_FREE_LIST . lock ( ) . deallocate ( range) . unwrap ( ) ;
161+ }
162+ }
163+
164+ /// Adds the given frame to the device free list.
165+ ///
166+ /// The identity mapping for the frame will be removed and a new mapping will be inserted at
167+ /// the offset for devices.
168+ ///
169+ /// The frame will then be added to the free list.
170+ ///
171+ /// # Safety
172+ ///
173+ /// The frame should have been clained from the free list
174+ unsafe fn map_claim_frame ( frame : PhysFrame < DeviceAllocIncrement > ) -> Result < ( ) , AllocError > {
175+ let identity_mapping = VirtAddr :: new ( frame. start_address ( ) . as_u64 ( ) ) ;
176+
177+ // Remove identity mapping
178+ paging:: unmap :: < DeviceAllocIncrement > ( identity_mapping, 1 ) ;
179+
180+ // Add mapping at the device offset
181+ let flags = PageTableEntryFlags :: WRITABLE
182+ | PageTableEntryFlags :: NO_EXECUTE
183+ | PageTableEntryFlags :: WRITE_THROUGH ;
184+
185+ let phys_addr = frame. start_address ( ) . into ( ) ;
186+ let virt_addr = VirtAddr :: from_ptr ( DeviceAlloc . ptr_from :: < ( ) > ( phys_addr) ) ;
187+ paging:: map :: < DeviceAllocIncrement > ( virt_addr, phys_addr, 1 , flags) ;
188+
189+ unsafe {
190+ DEVICE_FREE_LIST
191+ . lock ( )
192+ . deallocate ( frame. into ( ) )
193+ . map_err ( |_| AllocError )
69194 }
70195 }
71196}
0 commit comments