Untested work on physical mm setup
[tpg/acess2.git] / Kernel / arch / x86_64 / mm_phys.c
1 /*
2  * Acess2 x86_64 Port
3  * 
4  * Physical Memory Manager
5  */
6 #include <acess.h>
7 #include <mboot.h>
8 #include <mm_virt.h>
9
10 enum eMMPhys_Ranges
11 {
12         MM_PHYS_16BIT,  // Does anything need this?
13         MM_PHYS_20BIT,  // Real-Mode
14         MM_PHYS_24BIT,  // ISA DMA
15         MM_PHYS_32BIT,  // x86 Hardware
16         MM_PHYS_MAX,    // Doesn't care
17         NUM_MM_PHYS_RANGES
18 };
19
20 // === IMPORTS ===
21 extern void     gKernelEnd;
22
23 // === GLOBALS ===
24 tSpinlock       glPhysicalPages;
25 Uint64  *gaSuperBitmap; // 1 bit = 64 Pages
26 Uint32  *gaiPageReferences = (void*)MM_PAGE_COUNTS;     // Reference Counts
27 tPAddr  giFirstFreePage;        // First possibly free page
28 Uint64  giPhysRangeFree[NUM_MM_PHYS_RANGES];    // Number of free pages in each range
29 Uint64  giPhysRangeFirst[NUM_MM_PHYS_RANGES];   // First free page in each range
30 Uint64  giPhysRangeLast[NUM_MM_PHYS_RANGES];    // Last free page in each range
31 Uint64  giMaxPhysPage = 0;      // Maximum Physical page
32
33 // === CODE ===
34 void MM_InitPhys_Multiboot(tMBoot_Info *MBoot)
35 {
36         tMBoot_MMapEnt  *mmapStart;
37         tMBoot_MMapEnt  *ent;
38         Uint64  maxAddr = 0;
39          int    numPages;
40          int    superPages;
41         
42         Log("MM_InitPhys_Multiboot: (MBoot=%p)", MBoot);
43         
44         // Scan the physical memory map
45         // Looking for the top of physical memory
46         mmapStart = (void *)( KERNEL_BASE | MBoot->MMapAddr );
47         Log(" MM_InitPhys_Multiboot: mmapStart = %p", mmapStart);
48         ent = mmapStart;
49         while( (Uint)ent < (Uint)mmapStart + MBoot->MMapLength )
50         {
51                 // Adjust for the size of the entry
52                 ent->Size += 4;
53                 Log(" MM_InitPhys_Multiboot: ent={Type:%i,Base:0x%x,Length:%x",
54                         ent->Type, ent->Base, ent->Length);
55                 
56                 // If entry is RAM and is above `maxAddr`, change `maxAddr`
57                 if(ent->Type == 1 && ent->Base + ent->Length > maxAddr)
58                         maxAddr = ent->Base + ent->Length;
59                 
60                 // Go to next entry
61                 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
62         }
63         
64         // Did we find a valid end?
65         if(maxAddr == 0) {
66                 // No, darn, let's just use the HighMem hack
67                 giMaxPhysPage = (MBoot->HighMem >> 2) + 256;    // HighMem is a kByte value
68         }
69         else {
70                 // Goodie, goodie gumdrops
71                 giMaxPhysPage = maxAddr >> 12;
72         }
73         Log(" MM_InitPhys_Multiboot: giMaxPhysPage = 0x%x", giMaxPhysPage);
74         
75         // Find a contigous section of memory to hold it in
76         // - Starting from the end of the kernel
77         // - We also need a region for the super bitmap
78         superPages = ((giMaxPhysPage+64*8-1)/(64*8) + 0xFFF) >> 12;
79         numPages = (giMaxPhysPage + 7) * sizeof(*gaiPageReferences);
80         numPages = (numPages + 0xFFF) >> 12;
81         Log(" MM_InitPhys_Multiboot: numPages = %i", numPages);
82         if(maxAddr == 0)
83         {
84                 // Ok, naieve allocation, just put it after the kernel
85                 tVAddr  vaddr = MM_PAGE_COUNTS;
86                 tPAddr  paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
87                 while(numPages --)
88                 {
89                         MM_Map(vaddr, paddr);
90                         vaddr += 0x1000;
91                         paddr += 0x1000;
92                 }
93                 // Allocate the super bitmap
94                 gaSuperBitmap = (void*) MM_MapHWPages(
95                         paddr,
96                         ((giMaxPhysPage+64*8-1)/(64*8) + 0xFFF) >> 12
97                         );
98         }
99         // Scan for a nice range
100         else
101         {
102                  int    todo = numPages + superPages;
103                 tPAddr  paddr = 0;
104                 tVAddr  vaddr = MM_PAGE_COUNTS;
105                 // Scan!
106                 for(
107                         ent = mmapStart;
108                         (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
109                         ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
110                         )
111                 {
112                          int    avail;
113                         
114                         // RAM only please
115                         if( ent->Type != 1 )
116                                 continue;
117                         
118                         // Let's not put it below the kernel, shall we?
119                         if( ent->Base + ent->Size < (tPAddr)&gKernelEnd )
120                                 continue;
121                         
122                         // Check if the kernel is in this range
123                         if( ent->Base < (tPAddr)&gKernelEnd - KERNEL_BASE && ent->Base + ent->Size > (tPAddr)&gKernelEnd )
124                         {
125                                 avail = (ent->Size-((tPAddr)&gKernelEnd-KERNEL_BASE-ent->Base)) >> 12;
126                                 paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
127                         }
128                         // No? then we can use all of the block
129                         else
130                         {
131                                 avail = ent->Size >> 12;
132                                 paddr = ent->Base;
133                         }
134                         
135                         // Map
136                         // - Counts
137                         if( todo ) {
138                                  int    i, max;
139                                 max = todo < avail ? todo : avail;
140                                 for( i = 0; i < max; i ++ )
141                                 {
142                                         MM_Map(vaddr, paddr);
143                                         todo --;
144                                         vaddr += 0x1000;
145                                         paddr += 0x1000;
146                                 }
147                                 // Alter the destination address when needed
148                                 if(todo == superPages)
149                                         vaddr = MM_PAGE_SUPBMP;
150                         }
151                         else
152                                 break;
153                 }
154         }
155         
156         // Fill the bitmaps
157         // - initialise to one, then clear the avaliable areas
158         memset(gaSuperBitmap, -1, (giMaxPhysPage+64*8-1)/(64*8));
159         memset(gaiPageReferences, -1, giMaxPhysPage*sizeof(*gaiPageReferences));
160         // - Clear all Type=1 areas
161         for(
162                 ent = mmapStart;
163                 (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
164                 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
165                 )
166         {
167                 // Check if the type is RAM
168                 if(ent->Type != 1)      continue;
169                 // Clear the range
170                 memset(
171                         &gaiPageReferences[ ent->Base >> 12 ],
172                         0,
173                         (ent->Size>>12)*sizeof(*gaiPageReferences)
174                         );
175         }
176         
177         // Reference the used pages
178         // - Kernel
179         // - Reference Counts and Bitmap
180 }
181
182 /**
183  * \brief Allocate a contiguous range of physical pages with a maximum
184  *        bit size of \a Bits
185  * \param Num   Number of pages to allocate
186  * \param Bits  Maximum size of the physical address
187  * \note If \a Bits is <= 0, any sized address is used (with preference
188  *       to higher addresses)
189  */
190 tPAddr MM_AllocPhysRange(int Num, int Bits)
191 {
192         tPAddr  addr;
193          int    rangeID;
194          int    nFree = 0, i;
195         
196         Log("MM_AllocPhysRange: (Num=%i,Bits=%i)", Num, Bits);
197         
198         if( Bits <= 0 ) // Speedup for the common case
199                 rangeID = MM_PHYS_MAX;
200         else if( Bits > 32 )
201                 rangeID = MM_PHYS_MAX;
202         else if( Bits > 24 )
203                 rangeID = MM_PHYS_32BIT;
204         else if( Bits > 20 )
205                 rangeID = MM_PHYS_24BIT;
206         else if( Bits > 16 )
207                 rangeID = MM_PHYS_20BIT;
208         else
209                 rangeID = MM_PHYS_16BIT;
210         
211         Log(" MM_AllocPhysRange: rangeID = %i", rangeID);
212         
213         LOCK(&glPhysicalPages);
214         Log(" MM_AllocPhysRange: i has lock");
215         
216         // Check if the range actually has any free pages
217         while(giPhysRangeFree[rangeID] == 0 && rangeID)
218                 rangeID --;
219         
220         Log(" MM_AllocPhysRange: rangeID = %i", rangeID);
221         
222         // What the? Oh, man. No free pages
223         if(giPhysRangeFree[rangeID] == 0) {
224                 RELEASE(&glPhysicalPages);
225                 // TODO: Page out
226                 // ATM. Just Warning
227                 Warning(" MM_AllocPhysRange: Out of free pages");
228                 Log_Warning("Arch",
229                         "Out of memory (unable to fulfil request for %i pages), zero remaining",
230                         Num
231                         );
232                 return 0;
233         }
234         
235         // Check if there is enough in the range
236         if(giPhysRangeFree[rangeID] >= Num)
237         {
238                 // Do a cheap scan, scanning upwards from the first free page in
239                 // the range
240                 nFree = 1;
241                 addr = giPhysRangeFirst[ rangeID ];
242                 while( addr < giPhysRangeLast[ rangeID ] )
243                 {
244                         // Check the super bitmap
245                         if( gaSuperBitmap[addr >> (6+6)] == -1 ) {
246                                 nFree = 0;
247                                 addr += 1 << (6+6);
248                                 addr &= (1 << (6+6)) - 1;
249                                 continue;
250                         }
251                         // Check page block (64 pages)
252                         if( gaSuperBitmap[addr >> (6+6)] & (1 << (addr>>6)&63)) {
253                                 nFree = 0;
254                                 addr += 1 << (12+6);
255                                 addr &= (1 << (12+6)) - 1;
256                                 continue;
257                         }
258                         // Check individual page
259                         if( gaiPageReferences[addr] ) {
260                                 nFree = 0;
261                                 addr ++;
262                                 continue;
263                         }
264                         nFree ++;
265                         addr ++;
266                         if(nFree == Num)
267                                 break;
268                 }
269                 // If we don't find a contiguous block, nFree will not be equal
270                 // to Num, so we set it to zero and do the expensive lookup.
271                 if(nFree != Num)        nFree = 0;
272         }
273         
274         if( !nFree )
275         {
276                 // Oops. ok, let's do an expensive check (scan down the list
277                 // until a free range is found)
278                 nFree = 1;
279                 addr = giPhysRangeLast[ rangeID ];
280                 // TODO
281                 RELEASE(&glPhysicalPages);
282                 // TODO: Page out
283                 // ATM. Just Warning
284                 Log_Warning("Arch",
285                         "Out of memory (unable to fulfil request for %i pages)",
286                         Num
287                         );
288                 return 0;
289         }
290         Log(" MM_AllocPhysRange: nFree = %i, addr = 0x%08x", nFree, addr);
291         
292         // Mark pages as allocated
293         addr -= Num;
294         for( i = 0; i < Num; i++ )
295         {
296                 gaiPageReferences[addr] = 1;
297                 
298                      if(addr >> 32)     rangeID = MM_PHYS_MAX;
299                 else if(addr >> 24)     rangeID = MM_PHYS_32BIT;
300                 else if(addr >> 20)     rangeID = MM_PHYS_24BIT;
301                 else if(addr >> 16)     rangeID = MM_PHYS_20BIT;
302                 else if(addr >> 0)      rangeID = MM_PHYS_16BIT;
303                 giPhysRangeFree[ rangeID ] --;
304         }
305         // Fill super bitmap
306         Num += addr & (64-1);
307         addr &= ~(64-1);
308         Num = (Num + (64-1)) & ~(64-1);
309         for( i = 0; i < Num/64; i++ )
310         {
311                  int    j, bFull = 1;
312                 for( j = 0; j < 64; j ++ ) {
313                         if( gaiPageReferences[addr+i*64+j] ) {
314                                 bFull = 0;
315                                 break;
316                         }
317                 }
318                 if( bFull )
319                         gaSuperBitmap[addr>>12] |= 1 << ((addr >> 6) & 64);
320         }
321         
322         RELEASE(&glPhysicalPages);
323         return addr << 12;
324 }
325
326 /**
327  * \brief Allocate a single physical page, with no preference as to address
328  *        size.
329  */
330 tPAddr MM_AllocPhys(void)
331 {
332         return MM_AllocPhysRange(1, -1);
333 }
334
335 /**
336  * \brief Reference a physical page
337  */
338 void MM_RefPhys(tPAddr PAddr)
339 {
340          int    bIsFull, j;
341         if( PAddr >> 12 > giMaxPhysPage )       return ;
342         gaiPageReferences[ PAddr >> 12 ] ++;
343         
344         bIsFull = 1;
345         for( j = 0; j < 64; j++ ) {
346                 if( gaiPageReferences[ PAddr >> 12 ] == 0 ) {
347                         bIsFull = 0;
348                         break;
349                 }
350         }
351         if( bIsFull )
352                 gaSuperBitmap[PAddr >> 24] |= 1 << ((PAddr >> 18) & 64);
353 }
354
355 /**
356  * \brief Dereference a physical page
357  */
358 void MM_DerefPhys(tPAddr PAddr)
359 {
360         if( PAddr >> 12 > giMaxPhysPage )       return ;
361         gaiPageReferences[ PAddr >> 12 ] --;
362         if( gaiPageReferences[ PAddr >> 12 ] )
363         {
364                 gaSuperBitmap[PAddr >> 24] &= ~(1 << ((PAddr >> 18) & 64));
365         }
366 }

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