6
6
7
7
extern crate alloc;
8
8
9
+ use core:: slice:: from_raw_parts_mut;
10
+
11
+ use alloc:: sync:: Arc ;
9
12
use alloc:: vec:: Vec ;
10
13
11
- use super :: { RawAllocMapping , VirtualMapping } ;
14
+ use super :: { Mapping , RawAllocMapping , VMPageFaultResolution , VMPhysMem , VirtualMapping } ;
15
+ use crate :: address:: Address ;
12
16
use crate :: error:: SvsmError ;
13
17
use crate :: fs:: FileHandle ;
18
+ use crate :: mm:: vm:: VMR ;
14
19
use crate :: mm:: PageRef ;
15
20
use crate :: mm:: { pagetable:: PageTable , PAGE_SIZE } ;
16
21
use crate :: types:: PAGE_SHIFT ;
@@ -19,6 +24,16 @@ use crate::utils::align_up;
19
24
#[ derive( Debug ) ]
20
25
struct VMWriteFileMapping ( RawAllocMapping ) ;
21
26
27
+ impl VMWriteFileMapping {
28
+ pub fn get_alloc ( & self ) -> & RawAllocMapping {
29
+ & self . 0
30
+ }
31
+
32
+ pub fn get_alloc_mut ( & mut self ) -> & mut RawAllocMapping {
33
+ & mut self . 0
34
+ }
35
+ }
36
+
22
37
impl VirtualMapping for VMWriteFileMapping {
23
38
fn mapping_size ( & self ) -> usize {
24
39
self . 0 . mapping_size ( )
@@ -57,6 +72,9 @@ pub struct VMFileMapping {
57
72
58
73
/// A vec containing references to mapped pages within the file
59
74
pages : Vec < Option < PageRef > > ,
75
+
76
+ /// A copy of the file pages for mappings with Write permission
77
+ write_copy : Option < VMWriteFileMapping > ,
60
78
}
61
79
62
80
impl VMFileMapping {
@@ -96,12 +114,22 @@ impl VMFileMapping {
96
114
for page_index in 0 ..count {
97
115
pages. push ( file. mapping ( offset + page_index * PAGE_SIZE ) ) ;
98
116
}
117
+ // For ranges with write access we need to take a copy of the ram pages
118
+ // to allow them to be written to without modifying the contents of the
119
+ // file itself and also to prevent pointer aliasing with any other
120
+ // FileHandles that may be open on the same file.
121
+ let write_copy = if permission == VMFileMappingPermission :: Write {
122
+ Some ( VMWriteFileMapping ( RawAllocMapping :: new ( size) ) )
123
+ } else {
124
+ None
125
+ } ;
99
126
100
127
Ok ( Self {
101
128
file,
102
129
size,
103
130
permission,
104
131
pages,
132
+ write_copy,
105
133
} )
106
134
}
107
135
}
@@ -116,14 +144,67 @@ impl VirtualMapping for VMFileMapping {
116
144
if page_index >= self . pages . len ( ) {
117
145
return None ;
118
146
}
147
+ if let Some ( write_copy) = & self . write_copy {
148
+ let write_addr = write_copy. map ( offset) ;
149
+ if write_addr. is_some ( ) {
150
+ return write_addr;
151
+ }
152
+ }
119
153
self . pages [ page_index] . as_ref ( ) . map ( |p| p. phys_addr ( ) )
120
154
}
121
155
122
- fn pt_flags ( & self , _offset : usize ) -> crate :: mm:: pagetable:: PTEntryFlags {
156
+ fn pt_flags ( & self , offset : usize ) -> crate :: mm:: pagetable:: PTEntryFlags {
123
157
match self . permission {
124
158
VMFileMappingPermission :: Read => PageTable :: task_data_ro_flags ( ) ,
125
- VMFileMappingPermission :: Write => PageTable :: task_data_flags ( ) ,
159
+ VMFileMappingPermission :: Write => {
160
+ if let Some ( write_copy) = & self . write_copy {
161
+ if write_copy. get_alloc ( ) . present ( offset) {
162
+ PageTable :: task_data_flags ( )
163
+ } else {
164
+ PageTable :: task_data_ro_flags ( )
165
+ }
166
+ } else {
167
+ PageTable :: task_data_ro_flags ( )
168
+ }
169
+ }
126
170
VMFileMappingPermission :: Execute => PageTable :: task_exec_flags ( ) ,
127
171
}
128
172
}
173
+
174
+ fn handle_page_fault (
175
+ & mut self ,
176
+ vmr : & VMR ,
177
+ offset : usize ,
178
+ write : bool ,
179
+ ) -> Result < VMPageFaultResolution , SvsmError > {
180
+ let page_size = self . page_size ( ) ;
181
+ if write {
182
+ if let Some ( write_copy) = self . write_copy . as_mut ( ) {
183
+ // This is a writeable region with copy-on-write access. The
184
+ // page fault will have occurred because the page has not yet
185
+ // been allocated. Allocate a page and copy the readonly source
186
+ // page into the new writeable page.
187
+ let offset_aligned = offset & !( page_size - 1 ) ;
188
+ if write_copy
189
+ . get_alloc_mut ( )
190
+ . alloc_page ( offset_aligned)
191
+ . is_ok ( )
192
+ {
193
+ let paddr_new_page = write_copy. map ( offset_aligned) . ok_or ( SvsmError :: Mem ) ?;
194
+ let temp_map = VMPhysMem :: new ( paddr_new_page, page_size, true ) ;
195
+ let vaddr_new_page = vmr. insert ( Arc :: new ( Mapping :: new ( temp_map) ) ) ?;
196
+ let slice =
197
+ unsafe { from_raw_parts_mut ( vaddr_new_page. bits ( ) as * mut u8 , page_size) } ;
198
+ self . file . seek ( offset_aligned) ;
199
+ self . file . read ( slice) ?;
200
+ vmr. remove ( vaddr_new_page) ?;
201
+ return Ok ( VMPageFaultResolution {
202
+ paddr : paddr_new_page,
203
+ flags : PageTable :: task_data_flags ( ) ,
204
+ } ) ;
205
+ }
206
+ }
207
+ }
208
+ Err ( SvsmError :: Mem )
209
+ }
129
210
}
0 commit comments