1
1
//! Module instance types and functionality.
2
2
3
- use oro_mem:: mapper:: { AddressSegment , AddressSpace as _, MapError } ;
3
+ use oro_mem:: {
4
+ mapper:: { AddressSegment , AddressSpace as _, MapError } ,
5
+ phys:: PhysAddr ,
6
+ } ;
4
7
5
8
use crate :: {
6
9
AddressSpace , Kernel , UserHandle ,
@@ -63,6 +66,13 @@ pub struct Instance<A: Arch> {
63
66
/// If a token is here, the instance is allowed to map it
64
67
/// into its address space.
65
68
tokens : Table < Tab < Token > > ,
69
+ /// The virtual memory mappings of virtual addresses to tokens.
70
+ /// Values are `(token, page_id)` pairs.
71
+ // TODO(qix-): This assumes a 4096 byte page size, and is also
72
+ // TODO(qix-): not a very efficient data structure. It will be
73
+ // TODO(qix-): replaced with a more efficient data structure
74
+ // TODO(qix-): in the future.
75
+ token_vmap : Table < ( Tab < Token > , usize ) > ,
66
76
}
67
77
68
78
impl < A : Arch > Instance < A > {
@@ -103,6 +113,7 @@ impl<A: Arch> Instance<A> {
103
113
handle,
104
114
data : TypeTable :: new ( ) ,
105
115
tokens : Table :: new ( ) ,
116
+ token_vmap : Table :: new ( ) ,
106
117
} )
107
118
. ok_or ( MapError :: OutOfMemory ) ?;
108
119
@@ -151,11 +162,110 @@ impl<A: Arch> Instance<A> {
151
162
self . tokens . insert_tab ( token)
152
163
}
153
164
154
- /// Maps a [`Token`] into the instance's address space.
165
+ /// Maps (sets the base of and reserves) a [`Token`] into the instance's address space.
155
166
///
156
- /// Returns the address segment of the mapping.
157
- pub fn map_token ( & self , token : & Tab < Token > , virt : usize ) -> Result < ( ) , MapError > {
158
- todo ! ( "map token: {:016X} -> {virt:016X}" , token. id( ) ) ;
167
+ /// **This does not immediately map in any memory.** It only marks the range
168
+ /// in the _internal kernel_ address space as reserved for the token, to be
169
+ /// committed later (typically via a page fault calling [`Instance::try_commit_token_at`]).
170
+ pub fn try_map_token_at (
171
+ & mut self ,
172
+ token : & Tab < Token > ,
173
+ virt : usize ,
174
+ ) -> Result < ( ) , TokenMapError > {
175
+ if !self . tokens . contains ( token. id ( ) ) {
176
+ return Err ( TokenMapError :: BadToken ) ;
177
+ }
178
+
179
+ token. with ( |t| {
180
+ match t {
181
+ Token :: Normal ( t) => {
182
+ debug_assert ! (
183
+ t. page_size( ) == 4096 ,
184
+ "page size != 4096 is not implemented"
185
+ ) ;
186
+ debug_assert ! (
187
+ t. page_size( ) . is_power_of_two( ) ,
188
+ "page size is not a power of 2"
189
+ ) ;
190
+
191
+ if ( virt & ( t. page_size ( ) - 1 ) ) != 0 {
192
+ return Err ( TokenMapError :: VirtNotAligned ) ;
193
+ }
194
+
195
+ let segment = AddressSpace :: < A > :: user_data ( ) ;
196
+
197
+ // Make sure that none of the tokens exist in the vmap.
198
+ for page_idx in 0 ..t. page_count ( ) {
199
+ let page_base = virt + ( page_idx * t. page_size ( ) ) ;
200
+
201
+ if !virt_resides_within :: < A > ( & segment, page_base)
202
+ || !virt_resides_within :: < A > ( & segment, page_base + t. page_size ( ) - 1 )
203
+ {
204
+ return Err ( TokenMapError :: VirtOutOfRange ) ;
205
+ }
206
+
207
+ if self . token_vmap . contains (
208
+ u64:: try_from ( page_base) . map_err ( |_| TokenMapError :: VirtOutOfRange ) ?,
209
+ ) {
210
+ return Err ( TokenMapError :: Conflict ) ;
211
+ }
212
+ }
213
+
214
+ // Everything's okay, map them into the vmap now.
215
+ for page_idx in 0 ..t. page_count ( ) {
216
+ let page_base = virt + ( page_idx * t. page_size ( ) ) ;
217
+ // NOTE(qix-): We can use `as` here since we already check the page base above.
218
+ self . token_vmap
219
+ . insert ( page_base as u64 , ( token. clone ( ) , page_idx) ) ;
220
+ }
221
+
222
+ Ok ( ( ) )
223
+ }
224
+ }
225
+ } )
226
+ }
227
+
228
+ /// Commits a [`Token`] into the instance's address space at
229
+ /// the specified virtual address. Returns a mapping error
230
+ /// if the mapping could not be completed.
231
+ ///
232
+ /// **The `maybe_unaligned_virt` parameter is not guaranteed to be aligned
233
+ /// to any page boundary.** In most cases, it is coming directly from a
234
+ /// userspace application (typically via a fault).
235
+ ///
236
+ /// The `Token` must have been previously mapped via [`Instance::try_map_token_at`],
237
+ /// or else this method will fail.
238
+ pub fn try_commit_token_at ( & self , maybe_unaligned_virt : usize ) -> Result < ( ) , TryCommitError > {
239
+ // TODO(qix-): We always assume a 4096 page boundary. This will change in the future.
240
+ let virt = maybe_unaligned_virt & !0xFFF ;
241
+
242
+ if let Some ( ( token, page_idx) ) = u64:: try_from ( virt)
243
+ . ok ( )
244
+ . and_then ( |virt| self . token_vmap . get ( virt) )
245
+ {
246
+ token. with_mut ( |t| {
247
+ match t {
248
+ Token :: Normal ( t) => {
249
+ debug_assert ! ( * page_idx < t. page_count( ) ) ;
250
+ debug_assert ! (
251
+ t. page_size( ) == 4096 ,
252
+ "page size != 4096 is not implemented"
253
+ ) ;
254
+ let page_base = virt + ( * page_idx * t. page_size ( ) ) ;
255
+ let segment = AddressSpace :: < A > :: user_data ( ) ;
256
+ let phys = t
257
+ . get_or_allocate ( * page_idx)
258
+ . ok_or ( TryCommitError :: MapError ( MapError :: OutOfMemory ) ) ?;
259
+ segment
260
+ . map ( self . handle . mapper ( ) , page_base, phys. address_u64 ( ) )
261
+ . map_err ( TryCommitError :: MapError ) ?;
262
+ Ok ( ( ) )
263
+ }
264
+ }
265
+ } )
266
+ } else {
267
+ Err ( TryCommitError :: BadVirt )
268
+ }
159
269
}
160
270
161
271
/// Returns the instance's address space handle.
@@ -177,3 +287,37 @@ impl<A: Arch> Instance<A> {
177
287
& mut self . data
178
288
}
179
289
}
290
+
291
+ /// An error returned by [`Instance::try_map_token_at`].
292
+ #[ derive( Debug , Clone , Copy ) ]
293
+ pub enum TokenMapError {
294
+ /// The virtual address is not aligned.
295
+ VirtNotAligned ,
296
+ /// The virtual address is out of range for the
297
+ /// thread's address space.
298
+ VirtOutOfRange ,
299
+ /// The token was not found for the instance.
300
+ BadToken ,
301
+ /// The mapping conflicts (overlaps) with another mapping.
302
+ Conflict ,
303
+ }
304
+
305
+ /// Checks if the given virtual address resides within the given address segment.
306
+ #[ inline]
307
+ fn virt_resides_within < A : Arch > (
308
+ segment : & <AddressSpace < A > as :: oro_mem:: mapper:: AddressSpace >:: UserSegment ,
309
+ virt : usize ,
310
+ ) -> bool {
311
+ // NOTE(qix-): Range is *inclusive*.
312
+ let ( first, last) = segment. range ( ) ;
313
+ virt >= first && virt <= last
314
+ }
315
+
316
+ /// An error returned by [`Instance::try_commit_token_at`].
317
+ #[ derive( Debug , Clone , Copy ) ]
318
+ pub enum TryCommitError {
319
+ /// The virtual address was not found in the virtual map.
320
+ BadVirt ,
321
+ /// Mapping the token failed.
322
+ MapError ( MapError ) ,
323
+ }
0 commit comments