11use core:: { alloc:: Layout , convert:: TryInto } ;
2+ use rand:: distributions:: { Distribution , Uniform } ;
3+ use rand_chacha:: ChaCha20Rng ;
24use usize_conversions:: IntoUsize ;
35use x86_64:: {
46 structures:: paging:: { Page , PageTableIndex , Size4KiB } ,
@@ -7,15 +9,19 @@ use x86_64::{
79use xmas_elf:: program:: ProgramHeader ;
810
911use crate :: {
10- binary:: { MemoryRegion , CONFIG } ,
12+ binary:: { entropy , MemoryRegion , CONFIG } ,
1113 BootInfo ,
1214} ;
1315
1416/// Keeps track of used entries in a level 4 page table.
1517///
1618/// Useful for determining a free virtual memory block, e.g. for mapping additional data.
1719pub struct UsedLevel4Entries {
18- entry_state : [ bool ; 512 ] , // whether an entry is in use by the kernel
20+ /// Whether an entry is in use by the kernel.
21+ entry_state : [ bool ; 512 ] ,
22+ /// A random number generator that should be used to generate random addresses or
23+ /// `None` if aslr is disabled.
24+ rng : Option < ChaCha20Rng > ,
1925}
2026
2127impl UsedLevel4Entries {
@@ -25,6 +31,7 @@ impl UsedLevel4Entries {
2531 pub fn new ( max_phys_addr : PhysAddr , regions_len : usize , framebuffer_size : usize ) -> Self {
2632 let mut used = UsedLevel4Entries {
2733 entry_state : [ false ; 512 ] ,
34+ rng : CONFIG . aslr . then ( entropy:: build_rng) ,
2835 } ;
2936
3037 used. entry_state [ 0 ] = true ; // TODO: Can we do this dynamically?
@@ -104,23 +111,61 @@ impl UsedLevel4Entries {
104111 /// Since this method marks each returned index as used, it can be used multiple times
105112 /// to determine multiple unused virtual memory regions.
106113 pub fn get_free_entry ( & mut self ) -> PageTableIndex {
107- let ( idx, entry) = self
114+ // Create an iterator over all available p4 indices.
115+ let mut free_entries = self
108116 . entry_state
109- . iter_mut ( )
117+ . iter ( )
118+ . copied ( )
110119 . enumerate ( )
111- . find ( |( _, & mut entry) | entry == false )
112- . expect ( "no usable level 4 entries found" ) ;
120+ . filter ( |( _, used) | !used)
121+ . map ( |( idx, _) | idx) ;
122+
123+ // Choose the free entry index.
124+ let idx = if let Some ( rng) = self . rng . as_mut ( ) {
125+ // Count the entries and randomly choose an index in `[0..count)`.
126+ let count = free_entries. clone ( ) . count ( ) ;
127+ if count == 0 {
128+ panic ! ( "no usable level 4 entries found" )
129+ }
130+ let distribution = Uniform :: from ( 0 ..count) ;
131+ let idx = distribution. sample ( rng) ;
132+
133+ // Get the index of the free entry.
134+ free_entries. nth ( idx) . unwrap ( )
135+ } else {
136+ // Choose the first index.
137+ free_entries
138+ . next ( )
139+ . expect ( "no usable level 4 entries found" )
140+ } ;
141+
142+ // Mark the entry as used.
143+ self . entry_state [ idx] = true ;
113144
114- * entry = true ;
115145 PageTableIndex :: new ( idx. try_into ( ) . unwrap ( ) )
116146 }
117147
118- /// Returns the virtual start address of an unused level 4 entry and marks it as used.
148+ /// Returns a virtual address in an unused level 4 entry and marks it as used.
119149 ///
120- /// This is a convenience method around [`get_free_entry`], so all of its docs applies here
150+ /// This functions call [`get_free_entry`] internally , so all of its docs applies here
121151 /// too.
122- pub fn get_free_address ( & mut self ) -> VirtAddr {
123- Page :: from_page_table_indices_1gib ( self . get_free_entry ( ) , PageTableIndex :: new ( 0 ) )
124- . start_address ( )
152+ pub fn get_free_address ( & mut self , size : u64 , alignment : u64 ) -> VirtAddr {
153+ assert ! ( alignment. is_power_of_two( ) ) ;
154+
155+ let base =
156+ Page :: from_page_table_indices_1gib ( self . get_free_entry ( ) , PageTableIndex :: new ( 0 ) )
157+ . start_address ( ) ;
158+
159+ let offset = if let Some ( rng) = self . rng . as_mut ( ) {
160+ // Choose a random offset.
161+ const LEVEL_4_SIZE : u64 = 4096 * 512 * 512 * 512 ;
162+ let end = LEVEL_4_SIZE - size;
163+ let uniform_range = Uniform :: from ( 0 ..end / alignment) ;
164+ uniform_range. sample ( rng) * alignment
165+ } else {
166+ 0
167+ } ;
168+
169+ base + offset
125170 }
126171}
0 commit comments