Yay! TSS issues fixed, now to work on the VMM
[tpg/acess2.git] / Kernel / arch / x86_64 / mm_phys.c
1 /*
2  * Acess2 x86_64 Port
3  * 
4  * Physical Memory Manager
5  */
6 #define DEBUG   0
7 #include <acess.h>
8 #include <mboot.h>
9 #include <mm_virt.h>
10
11 enum eMMPhys_Ranges
12 {
13         MM_PHYS_16BIT,  // Does anything need this?
14         MM_PHYS_20BIT,  // Real-Mode
15         MM_PHYS_24BIT,  // ISA DMA
16         MM_PHYS_32BIT,  // x86 Hardware
17         MM_PHYS_MAX,    // Doesn't care
18         NUM_MM_PHYS_RANGES
19 };
20
21 // === IMPORTS ===
22 extern void     gKernelBase;
23 extern void     gKernelEnd;
24
25 // === PROTOTYPES ===
26 void    MM_InitPhys_Multiboot(tMBoot_Info *MBoot);
27 tPAddr  MM_AllocPhysRange(int Num, int Bits);
28 tPAddr  MM_AllocPhys(void);
29 void    MM_RefPhys(tPAddr PAddr);
30 void    MM_DerefPhys(tPAddr PAddr);
31  int    MM_int_GetRangeID( tPAddr Addr );
32
33 // === GLOBALS ===
34 tSpinlock       glPhysicalPages;
35 Uint64  *gaSuperBitmap; // 1 bit = 64 Pages, 16 MiB Per Word
36 Uint64  *gaMainBitmap;  // 1 bit = 1 Page, 256 KiB per Word
37 Uint64  *gaMultiBitmap; // Each bit means that the page is being used multiple times
38 Uint32  *gaiPageReferences = (void*)MM_PAGE_COUNTS;     // Reference Counts
39 tPAddr  giFirstFreePage;        // First possibly free page
40 Uint64  giPhysRangeFree[NUM_MM_PHYS_RANGES];    // Number of free pages in each range
41 Uint64  giPhysRangeFirst[NUM_MM_PHYS_RANGES];   // First free page in each range
42 Uint64  giPhysRangeLast[NUM_MM_PHYS_RANGES];    // Last free page in each range
43 Uint64  giMaxPhysPage = 0;      // Maximum Physical page
44 // Only used in init, allows the init code to provide pages for use by
45 // the allocator before the bitmaps exist.
46 // 3 entries because the are three calls to MM_AllocPhys in MM_Map
47 #define NUM_STATIC_ALLOC        3
48 tPAddr  gaiStaticAllocPages[NUM_STATIC_ALLOC] = {0};
49
50 // === CODE ===
51 /**
52  * \brief Initialise the physical memory map using a Multiboot 1 map
53  */
54 void MM_InitPhys_Multiboot(tMBoot_Info *MBoot)
55 {
56         tMBoot_MMapEnt  *mmapStart;
57         tMBoot_MMapEnt  *ent;
58         Uint64  maxAddr = 0;
59          int    numPages, superPages;
60          int    i;
61         Uint64  base, size;
62         tVAddr  vaddr;
63         tPAddr  paddr, firstFreePage;
64         
65         ENTER("pMBoot=%p", MBoot);
66         
67         // Scan the physical memory map
68         // Looking for the top of physical memory
69         mmapStart = (void *)( KERNEL_BASE | MBoot->MMapAddr );
70         LOG("mmapStart = %p", mmapStart);
71         ent = mmapStart;
72         while( (Uint)ent < (Uint)mmapStart + MBoot->MMapLength )
73         {
74                 // Adjust for the size of the entry
75                 ent->Size += 4;
76                 LOG("ent={Type:%i,Base:0x%x,Length:%x",
77                         ent->Type, ent->Base, ent->Length);
78                 
79                 // If entry is RAM and is above `maxAddr`, change `maxAddr`
80                 if(ent->Type == 1 && ent->Base + ent->Length > maxAddr)
81                         maxAddr = ent->Base + ent->Length;
82                 
83                 // Go to next entry
84                 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
85         }
86         
87         // Did we find a valid end?
88         if(maxAddr == 0) {
89                 // No, darn, let's just use the HighMem hack
90                 giMaxPhysPage = (MBoot->HighMem >> 2) + 256;    // HighMem is a kByte value
91         }
92         else {
93                 // Goodie, goodie gumdrops
94                 giMaxPhysPage = maxAddr >> 12;
95         }
96         LOG("giMaxPhysPage = 0x%x", giMaxPhysPage);
97         
98         // Find a contigous section of memory to hold it in
99         // - Starting from the end of the kernel
100         // - We also need a region for the super bitmap
101         superPages = ((giMaxPhysPage+64*8-1)/(64*8) + 0xFFF) >> 12;
102         numPages = (giMaxPhysPage + 7) / 8;
103         numPages = (numPages + 0xFFF) >> 12;
104         LOG("numPages = %i, superPages = %i", numPages, superPages);
105         if(maxAddr == 0)
106         {
107                  int    todo = numPages*2 + superPages;
108                 // Ok, naieve allocation, just put it after the kernel
109                 // - Allocated Bitmap
110                 vaddr = MM_PAGE_BITMAP;
111                 paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
112                 while(todo )
113                 {
114                         // Allocate statics
115                         for( i = 0; i < NUM_STATIC_ALLOC; i++) {
116                                 if(gaiStaticAllocPages[i] != 0) continue;
117                                 gaiStaticAllocPages[i] = paddr;
118                                 paddr += 0x1000;
119                         }
120                         
121                         MM_Map(vaddr, paddr);
122                         vaddr += 0x1000;
123                         paddr += 0x1000;
124                         
125                         todo --;
126                         
127                         if( todo == numPages + superPages )
128                                 vaddr = MM_PAGE_DBLBMP;
129                         if( todo == superPages )
130                                 vaddr = MM_PAGE_SUPBMP;
131                 }
132         }
133         // Scan for a nice range
134         else
135         {
136                  int    todo = numPages*2 + superPages;
137                 paddr = 0;
138                 vaddr = MM_PAGE_BITMAP;
139                 // Scan!
140                 for(
141                         ent = mmapStart;
142                         (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
143                         ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
144                         )
145                 {
146                          int    avail;
147                         
148                         // RAM only please
149                         if( ent->Type != 1 )
150                                 continue;
151                         
152                         // Let's not put it below the kernel, shall we?
153                         if( ent->Base + ent->Size < (tPAddr)&gKernelBase )
154                                 continue;
155                         
156                         LOG("%x <= %x && %x > %x",
157                                 ent->Base, (tPAddr)&gKernelBase,
158                                 ent->Base + ent->Size, (tPAddr)&gKernelEnd - KERNEL_BASE
159                                 );
160                         // Check if the kernel is in this range
161                         if( ent->Base <= (tPAddr)&gKernelBase
162                         && ent->Base + ent->Length > (tPAddr)&gKernelEnd - KERNEL_BASE )
163                         {
164                                 avail = ent->Length >> 12;
165                                 avail -= ((tPAddr)&gKernelEnd - KERNEL_BASE - ent->Base) >> 12;
166                                 paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
167                         }
168                         // No? then we can use all of the block
169                         else
170                         {
171                                 avail = ent->Length >> 12;
172                                 paddr = ent->Base;
173                         }
174                         
175                         Log(" MM_InitPhys_Multiboot: paddr=0x%x, avail=%i", paddr, avail);
176                         
177                         // Map
178                         while( todo && avail --)
179                         {
180                                 // Static Allocations
181                                 for( i = 0; i < NUM_STATIC_ALLOC && avail; i++) {
182                                         if(gaiStaticAllocPages[i] != 0) continue;
183                                         gaiStaticAllocPages[i] = paddr;
184                                         paddr += 0x1000;
185                                         avail --;
186                                 }
187                                 if(!avail)      break;
188                                 
189                                 // Map
190                                 MM_Map(vaddr, paddr);
191                                 todo --;
192                                 vaddr += 0x1000;
193                                 paddr += 0x1000;
194                                 
195                                 // Alter the destination address when needed
196                                 if(todo == superPages+numPages)
197                                         vaddr = MM_PAGE_DBLBMP;
198                                 if(todo == superPages)
199                                         vaddr = MM_PAGE_SUPBMP;
200                         }
201                         
202                         // Fast quit if there's nothing left to allocate
203                         if( !todo )             break;
204                 }
205         }
206         // Save the current value of paddr to simplify the allocation later
207         firstFreePage = paddr;
208         
209         LOG("Clearing multi bitmap");
210         // Fill the bitmaps
211         memset(gaMultiBitmap, 0, numPages<<12);
212         // - initialise to one, then clear the avaliable areas
213         memset(gaMainBitmap, -1, numPages<<12);
214         LOG("Setting main bitmap");
215         // - Clear all Type=1 areas
216         LOG("Clearing valid regions");
217         for(
218                 ent = mmapStart;
219                 (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
220                 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
221                 )
222         {
223                 // Check if the type is RAM
224                 if(ent->Type != 1)      continue;
225                 
226                 // Main bitmap
227                 base = ent->Base >> 12;
228                 size = ent->Size >> 12;
229                 
230                 if(base & 63) {
231                         Uint64  val = -1 << (base & 63);
232                         gaMainBitmap[base / 64] &= ~val;
233                         size -= (base & 63);
234                         base += 64 - (base & 63);
235                 }
236                 memset( &gaMainBitmap[base / 64], 0, size/8 );
237                 if( size & 7 ) {
238                         Uint64  val = -1 << (size & 7);
239                         val <<= (size/8)&7;
240                         gaMainBitmap[base / 64] &= ~val;
241                 }
242                 
243                 // Super Bitmap
244                 base = ent->Base >> 12;
245                 size = ent->Size >> 12;
246                 size = (size + (base & 63) + 63) >> 6;
247                 base = base >> 6;
248                 if(base & 63) {
249                         Uint64  val = -1 << (base & 63);
250                         gaSuperBitmap[base / 64] &= ~val;
251                         size -= (base & 63);
252                         base += 64 - (base & 63);
253                 }
254         }
255         
256         // Reference the used pages
257         base = (tPAddr)&gKernelBase >> 12;
258         size = firstFreePage >> 12;
259         memset( &gaMainBitmap[base / 64], -1, size/8 );
260         if( size & 7 ) {
261                 Uint64  val = -1 << (size & 7);
262                 val <<= (size/8)&7;
263                 gaMainBitmap[base / 64] |= val;
264         }
265         
266         // Free the unused static allocs
267         for( i = 0; i < NUM_STATIC_ALLOC; i++) {
268                 if(gaiStaticAllocPages[i] != 0)
269                         continue;
270                 gaMainBitmap[ gaiStaticAllocPages[i] >> (12+6) ]
271                         &= ~(1 << ((gaiStaticAllocPages[i]>>12)&63));
272         }
273         
274         // Fill the super bitmap
275         LOG("Filling super bitmap");
276         memset(gaSuperBitmap, 0, superPages<<12);
277         for( base = 0; base < (size+63)/64; base ++)
278         {
279                 if( gaMainBitmap[ base ] == -1 )
280                         gaSuperBitmap[ base/64 ] |= 1 << (base&63);
281         }
282         
283         // Set free page counts
284         for( base = 1; base < giMaxPhysPage; base ++ )
285         {
286                  int    rangeID;
287                 // Skip allocated
288                 if( gaMainBitmap[ base >> 6 ] & (1 << (base&63))  )     continue;
289                 
290                 // Get range ID
291                 rangeID = MM_int_GetRangeID( base << 12 );
292                 
293                 // Increment free page count
294                 giPhysRangeFree[ rangeID ] ++;
295                 
296                 // Check for first free page in range
297                 if(giPhysRangeFirst[ rangeID ] == 0)
298                         giPhysRangeFirst[ rangeID ] = base;
299                 // Set last (when the last free page is reached, this won't be
300                 // updated anymore, hence will be correct)
301                 giPhysRangeLast[ rangeID ] = base;
302         }
303         
304         LEAVE('-');
305 }
306
307 /**
308  * \brief Allocate a contiguous range of physical pages with a maximum
309  *        bit size of \a Bits
310  * \param Num   Number of pages to allocate
311  * \param Bits  Maximum size of the physical address
312  * \note If \a Bits is <= 0, any sized address is used (with preference
313  *       to higher addresses)
314  */
315 tPAddr MM_AllocPhysRange(int Num, int Bits)
316 {
317         tPAddr  addr, ret;
318          int    rangeID;
319          int    nFree = 0, i;
320         
321         ENTER("iNum iBits", Num, Bits);
322         
323         if( Bits <= 0 || Bits >= 64 )   // Speedup for the common case
324                 rangeID = MM_PHYS_MAX;
325         else
326                 rangeID = MM_int_GetRangeID( (1 << Bits) -1 );
327         
328         LOG("rangeID = %i", rangeID);
329         
330         LOCK(&glPhysicalPages);
331         
332         // Check if the range actually has any free pages
333         while(giPhysRangeFree[rangeID] == 0 && rangeID)
334                 rangeID --;
335         
336         LOG("rangeID = %i", rangeID);
337         
338         // What the? Oh, man. No free pages
339         if(giPhysRangeFree[rangeID] == 0) {
340                 RELEASE(&glPhysicalPages);
341                 // TODO: Page out
342                 // ATM. Just Warning
343                 Warning(" MM_AllocPhysRange: Out of free pages");
344                 Log_Warning("Arch",
345                         "Out of memory (unable to fulfil request for %i pages), zero remaining",
346                         Num
347                         );
348                 LEAVE('i', 0);
349                 return 0;
350         }
351         
352         // Check if there is enough in the range
353         if(giPhysRangeFree[rangeID] >= Num)
354         {
355                 LOG("{%i,0x%x -> 0x%x}",
356                         giPhysRangeFree[rangeID],
357                         giPhysRangeFirst[rangeID], giPhysRangeLast[rangeID]
358                         );
359                 // Do a cheap scan, scanning upwards from the first free page in
360                 // the range
361                 nFree = 0;
362                 addr = giPhysRangeFirst[ rangeID ];
363                 while( addr <= giPhysRangeLast[ rangeID ] )
364                 {
365                         //Log(" MM_AllocPhysRange: addr = 0x%x", addr);
366                         // Check the super bitmap
367                         if( gaSuperBitmap[addr >> (6+6)] == -1 ) {
368                                 LOG("nFree = %i = 0 (super) (0x%x)", nFree, addr);
369                                 nFree = 0;
370                                 addr += 1 << (6+6);
371                                 addr &= (1 << (6+6)) - 1;
372                                 continue;
373                         }
374                         // Check page block (64 pages)
375                         if( gaSuperBitmap[addr >> (6+6)] & (1 << (addr>>6)&63)) {
376                                 LOG("nFree = %i = 0 (main) (0x%x)", nFree, addr);
377                                 nFree = 0;
378                                 addr += 1 << (12+6);
379                                 addr &= (1 << (12+6)) - 1;
380                                 continue;
381                         }
382                         // Check individual page
383                         if( gaMainBitmap[addr >> 6] & (1 << (addr & 63)) ) {
384                                 LOG("nFree = %i = 0 (page) (0x%x)", nFree, addr);
385                                 nFree = 0;
386                                 addr ++;
387                                 continue;
388                         }
389                         nFree ++;
390                         addr ++;
391                         LOG("nFree(%i) == %i (0x%x)", nFree, Num, addr);
392                         if(nFree == Num)
393                                 break;
394                 }
395                 LOG("nFree = %i", nFree);
396                 // If we don't find a contiguous block, nFree will not be equal
397                 // to Num, so we set it to zero and do the expensive lookup.
398                 if(nFree != Num)        nFree = 0;
399         }
400         
401         if( !nFree )
402         {
403                 // Oops. ok, let's do an expensive check (scan down the list
404                 // until a free range is found)
405                 nFree = 1;
406                 addr = giPhysRangeLast[ rangeID ];
407                 // TODO
408                 RELEASE(&glPhysicalPages);
409                 // TODO: Page out
410                 // ATM. Just Warning
411                 Warning(" MM_AllocPhysRange: Out of memory (unable to fulfil request for %i pages)", Num);
412                 Log_Warning("Arch",
413                         "Out of memory (unable to fulfil request for %i pages)",
414                         Num
415                         );
416                 LEAVE('i', 0);
417                 return 0;
418         }
419         LOG("nFree = %i, addr = 0x%08x", nFree, addr);
420         
421         // Mark pages as allocated
422         addr -= Num;
423         for( i = 0; i < Num; i++, addr++ )
424         {
425                 gaMainBitmap[addr >> 6] |= 1 << (addr & 63);
426                 rangeID = MM_int_GetRangeID(addr);
427                 giPhysRangeFree[ rangeID ] --;
428         }
429         ret = addr;     // Save the return address
430         
431         // Update super bitmap
432         Num += addr & (64-1);
433         addr &= ~(64-1);
434         Num = (Num + (64-1)) & ~(64-1);
435         for( i = 0; i < Num/64; i++ )
436         {
437                 if( gaMainBitmap[ addr >> 6 ] == -1 )
438                         gaSuperBitmap[addr>>12] |= 1 << ((addr >> 6) & 64);
439         }
440         
441         RELEASE(&glPhysicalPages);
442         LEAVE('x', ret << 12);
443         return ret << 12;
444 }
445
446 /**
447  * \brief Allocate a single physical page, with no preference as to address
448  *        size.
449  */
450 tPAddr MM_AllocPhys(void)
451 {
452          int    i;
453         
454         // Hack to allow allocation during setup
455         for(i = 0; i < NUM_STATIC_ALLOC; i++) {
456                 if( gaiStaticAllocPages[i] ) {
457                         tPAddr  ret = gaiStaticAllocPages[i];
458                         gaiStaticAllocPages[i] = 0;
459                         Log("MM_AllocPhys: Return %x, static alloc %i", ret, i);
460                         return ret;
461                 }
462         }
463         
464         return MM_AllocPhysRange(1, -1);
465 }
466
467 /**
468  * \brief Reference a physical page
469  */
470 void MM_RefPhys(tPAddr PAddr)
471 {
472         Uint64  page = PAddr >> 12;
473         
474         if( PAddr >> 12 > giMaxPhysPage )       return ;
475         
476         if( gaMainBitmap[ page >> 6 ] & (1 << (page&63)) )
477         {
478                 // Reference again
479                 gaMultiBitmap[ page >> 6 ] |= 1 << (page&63);
480                 gaiPageReferences[ page ] ++;
481         }
482         else
483         {
484                 // Allocate
485                 gaMainBitmap[page >> 6] |= 1 << (page&63);
486                 if( gaMainBitmap[page >> 6 ] == -1 )
487                         gaSuperBitmap[page>> 12] |= 1 << ((page >> 6) & 63);
488         }
489 }
490
491 /**
492  * \brief Dereference a physical page
493  */
494 void MM_DerefPhys(tPAddr PAddr)
495 {
496         Uint64  page = PAddr >> 12;
497         
498         if( PAddr >> 12 > giMaxPhysPage )       return ;
499         
500         if( gaMultiBitmap[ page >> 6 ] & (1 << (page&63)) ) {
501                 gaiPageReferences[ page ] --;
502                 if( gaiPageReferences[ page ] == 1 )
503                         gaMultiBitmap[ page >> 6 ] &= ~(1 << (page&63));
504                 if( gaiPageReferences[ page ] == 0 )
505                         gaMainBitmap[ page >> 6 ] &= ~(1 << (page&63));
506         }
507         else
508                 gaMainBitmap[ page >> 6 ] &= ~(1 << (page&63));
509         
510         // TODO: Update free counts
511         if( !(gaMainBitmap[ page >> 6 ] & (1 << (page&63))) )
512         {
513                  int    rangeID;
514                 rangeID = MM_int_GetRangeID( PAddr );
515                 giPhysRangeFree[ rangeID ] ++;
516         }
517         
518         if(gaMainBitmap[ page >> 6 ] == 0) {
519                 gaSuperBitmap[page >> 12] &= ~(1 << ((page >> 6) & 63));
520         }
521 }
522
523 /**
524  * \brief Takes a physical address and returns the ID of its range
525  * \param Addr  Physical address of page
526  * \return Range ID from eMMPhys_Ranges
527  */
528 int MM_int_GetRangeID( tPAddr Addr )
529 {
530         if(Addr >> 32)
531                 return MM_PHYS_MAX;
532         else if(Addr >> 24)
533                 return MM_PHYS_32BIT;
534         else if(Addr >> 20)
535                 return MM_PHYS_24BIT;
536         else if(Addr >> 16)
537                 return MM_PHYS_20BIT;
538         else
539                 return MM_PHYS_16BIT;
540 }

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