3539f37c9755b4222178a60a5ff1248bbe687c19
[tpg/acess2.git] / KernelLand / Kernel / vfs / mmap.c
1 /*
2  * Acess2 Kernel VFS
3  * - By John Hodge (thePowersGang)
4  *
5  * mmap.c
6  * - VFS_MMap support
7  */
8 #define DEBUG   0
9 #include <acess.h>
10 #include <vfs.h>
11 #include <vfs_ext.h>
12 #include <vfs_int.h>
13
14 #define MMAP_PAGES_PER_BLOCK    16
15
16 // === STRUCTURES ===
17 typedef struct sVFS_MMapPageBlock       tVFS_MMapPageBlock;
18 struct sVFS_MMapPageBlock
19 {
20         tVFS_MMapPageBlock      *Next;
21         Uint64  BaseOffset;     // Must be a multiple of MMAP_PAGES_PER_BLOCK*PAGE_SIZE
22         tPAddr  PhysAddrs[MMAP_PAGES_PER_BLOCK];
23 };
24
25 // === PROTOTYPES ===
26 //void  *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, Uint64 Offset);
27 void    *VFS_MMap_Anon(void *Destination, size_t Length, Uint FlagsSet, Uint FlagsMask);
28
29 // === CODE ===
30 void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, Uint64 Offset)
31 {
32         tVAddr  mapping_base;
33          int    npages, pagenum;
34         tVFS_MMapPageBlock      *pb, *prev;
35
36         ENTER("pDestHint iLength xProtection xFlags xFD XOffset", DestHint, Length, Protection, Flags, FD, Offset);
37
38         if( Flags & MMAP_MAP_ANONYMOUS )
39                 Offset = (tVAddr)DestHint & 0xFFF;
40         
41         npages = ((Offset & (PAGE_SIZE-1)) + Length + (PAGE_SIZE - 1)) / PAGE_SIZE;
42         pagenum = Offset / PAGE_SIZE;
43
44         mapping_base = (tVAddr)DestHint;
45         tPage   *mapping_dest = (void*)(mapping_base & ~(PAGE_SIZE-1));
46
47         if( DestHint == NULL )
48         {
49                 // TODO: Locate space for the allocation
50                 LEAVE('n');
51                 return NULL;
52         }
53
54         // Handle anonymous mappings
55         if( Flags & MMAP_MAP_ANONYMOUS )
56         {
57                 // TODO: Comvert \a Protection into a flag set
58                 void    *ret = VFS_MMap_Anon((void*)mapping_base, Length, 0, 0);
59                 LEAVE_RET('p', ret);
60         }
61
62         tVFS_Handle *h = VFS_GetHandle(FD);
63         if( !h || !h->Node )    LEAVE_RET('n', NULL);
64
65         LOG("h = %p", h);
66         
67         Mutex_Acquire( &h->Node->Lock );
68
69         // Search for existing mapping for each page
70         // - Sorted list of 16 page blocks
71         for(
72                 pb = h->Node->MMapInfo, prev = NULL;
73                 pb && pb->BaseOffset + MMAP_PAGES_PER_BLOCK < pagenum;
74                 prev = pb, pb = pb->Next
75                 )
76                 ;
77
78         LOG("pb = %p, pb->BaseOffset = %X", pb, pb ? pb->BaseOffset : 0);
79
80         // - Allocate a block if needed
81         if( !pb || pb->BaseOffset > pagenum )
82         {
83                 void    *old_pb = pb;
84                 pb = calloc( 1, sizeof(tVFS_MMapPageBlock) );
85                 if(!pb) {
86                         Mutex_Release( &h->Node->Lock );
87                         LEAVE_RET('n', NULL);
88                 }
89                 pb->Next = old_pb;
90                 pb->BaseOffset = pagenum - pagenum % MMAP_PAGES_PER_BLOCK;
91                 if(prev)
92                         prev->Next = pb;
93                 else
94                         h->Node->MMapInfo = pb;
95         }
96
97         // - Map (and allocate) pages
98         while( npages -- )
99         {
100                 if( MM_GetPhysAddr( mapping_dest ) == 0 )
101                 {
102                         if( pb->PhysAddrs[pagenum - pb->BaseOffset] == 0 )
103                         {
104                                 tVFS_NodeType   *nt = h->Node->Type;
105                                 if( !nt ) 
106                                 {
107                                         // TODO: error
108                                 }
109                                 else if( nt->MMap )
110                                         nt->MMap(h->Node, pagenum*PAGE_SIZE, PAGE_SIZE, mapping_dest);
111                                 else
112                                 {
113                                          int    read_len;
114                                         // Allocate pages and read data
115                                         if( MM_Allocate(mapping_dest) == 0 ) {
116                                                 // TODO: Unwrap
117                                                 Mutex_Release( &h->Node->Lock );
118                                                 LEAVE('n');
119                                                 return NULL;
120                                         }
121                                         // TODO: Clip read length
122                                         read_len = nt->Read(h->Node, pagenum*PAGE_SIZE, PAGE_SIZE,
123                                                 mapping_dest, 0);
124                                         // TODO: This was commented out, why?
125                                         if( read_len != PAGE_SIZE ) {
126                                                 memset( (char*)mapping_dest + read_len, 0, PAGE_SIZE-read_len );
127                                         }
128                                 }
129                                 pb->PhysAddrs[pagenum - pb->BaseOffset] = MM_GetPhysAddr( mapping_dest );
130                                 MM_SetPageNode( pb->PhysAddrs[pagenum - pb->BaseOffset], h->Node );
131                                 MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] );
132                                 LOG("Read and map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest,
133                                         pb->PhysAddrs[pagenum - pb->BaseOffset]);
134                         }
135                         else
136                         {
137                                 MM_Map( mapping_dest, pb->PhysAddrs[pagenum - pb->BaseOffset] );
138                                 MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] );
139                                 LOG("Cached map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest,
140                                         pb->PhysAddrs[pagenum - pb->BaseOffset]);
141                         }
142                         h->Node->ReferenceCount ++;
143                 
144                         // Set flags
145                         if( !(Protection & MMAP_PROT_WRITE) ) {
146                                 MM_SetFlags(mapping_dest, MM_PFLAG_RO, MM_PFLAG_RO);
147                         }
148                         else {
149                                 MM_SetFlags(mapping_dest, 0, MM_PFLAG_RO);
150                         }
151                         
152                         if( Protection & MMAP_PROT_EXEC ) {
153                                 MM_SetFlags(mapping_dest, MM_PFLAG_EXEC, MM_PFLAG_EXEC);
154                         }
155                         else {
156                                 MM_SetFlags(mapping_dest, 0, MM_PFLAG_EXEC);
157                         }
158                 }
159                 else
160                 {
161                         LOG("Flag update on %p", mapping_dest);
162                         if( (MM_GetFlags(mapping_dest) & MM_PFLAG_RO) && (Protection & MMAP_PROT_WRITE) )
163                         {
164                                 MM_SetFlags(mapping_dest, 0, MM_PFLAG_RO);
165                         }
166                 }
167                 if( Flags & MMAP_MAP_PRIVATE )
168                         MM_SetFlags(mapping_dest, MM_PFLAG_COW, MM_PFLAG_COW);
169                 pagenum ++;
170                 mapping_dest ++;
171
172                 // Roll on to next block if needed
173                 if(pagenum - pb->BaseOffset == MMAP_PAGES_PER_BLOCK)
174                 {
175                         if( pb->Next && pb->Next->BaseOffset == pagenum )
176                                 pb = pb->Next;
177                         else
178                         {
179                                 tVFS_MMapPageBlock      *oldpb = pb;
180                                 pb = malloc( sizeof(tVFS_MMapPageBlock) );
181                                 pb->Next = oldpb->Next;
182                                 pb->BaseOffset = pagenum;
183                                 memset(pb->PhysAddrs, 0, sizeof(pb->PhysAddrs));
184                                 oldpb->Next = pb;
185                         }
186                 }
187         }
188         
189         Mutex_Release( &h->Node->Lock );
190
191         LEAVE('p', mapping_base);
192         return (void*)mapping_base;
193 }
194
195 void *VFS_MMap_Anon(void *Destination, size_t Length, Uint FlagsSet, Uint FlagsMask)
196 {
197         size_t  ofs = (tVAddr)Destination & (PAGE_SIZE-1);
198         tPage   *mapping_dest = (void*)( (char*)Destination - ofs );
199         
200         if( ofs > 0 )
201         {
202                 size_t  bytes = MIN(PAGE_SIZE - ofs, Length);
203                 
204                 // Allocate a partial page
205                 if( MM_GetPhysAddr(mapping_dest) )
206                 {
207                         // Already allocated page, clear the area we're touching
208                         ASSERT( ofs + bytes <= PAGE_SIZE );
209                         
210                         // TODO: Double check that this area isn't already zero
211                         memset( Destination, 0, bytes );
212                         
213                         MM_SetFlags(mapping_dest, FlagsSet, FlagsMask);
214                         
215                         LOG("#1: Clear %i from %p", Length, Destination);
216                 }
217                 else
218                 {
219                         MM_AllocateZero(mapping_dest);
220                         LOG("#1: Allocate for %p", Destination);
221                 }
222                 mapping_dest ++;
223                 Length -= bytes;
224         }
225         while( Length >= PAGE_SIZE )
226         {
227                 if( MM_GetPhysAddr( mapping_dest ) )
228                 {
229                         // We're allocating entire pages here, so free this page and replace with a COW zero
230                         MM_Deallocate(mapping_dest);
231                         LOG("Replace %p with zero page", mapping_dest);
232                 }
233                 else
234                 {
235                         LOG("Allocate zero at %p", mapping_dest);
236                 }
237                 MM_AllocateZero(mapping_dest);
238                 
239                 mapping_dest ++;
240                 Length -= PAGE_SIZE;
241         }
242         if( Length > 0 )
243         {
244                 ASSERT(Length < PAGE_SIZE);
245                 
246                 // Tail page
247                 if( MM_GetPhysAddr(mapping_dest) )
248                 {
249                         // TODO: Don't touch page if already zero
250                         memset( mapping_dest, 0, Length );
251                         LOG("Clear %i in %p", Length, mapping_dest);
252                 }
253                 else
254                 {
255                         MM_AllocateZero(mapping_dest);
256                         LOG("Anon map to %p", mapping_dest);
257                 }
258         }
259         
260         return Destination;
261 }
262
263 int VFS_MUnmap(void *Addr, size_t Length)
264 {
265         return 0;
266 }

UCC git Repository :: git.ucc.asn.au