e5c28c2f622feaa9ca847e0e2d842d84e775b511
[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 char     gKernelBase[];
23 extern char     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 = -1LL << (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 = -1LL << (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 = -1LL << (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 = -1LL << (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                         &= ~(1LL << ((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 == 0 )
280                         gaSuperBitmap[ base/64 ] |= 1LL << (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 ] & (1LL << (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( (1LL << 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 == 0 ) {
368                                 LOG("nFree = %i = 0 (super) (0x%x)", nFree, addr);
369                                 nFree = 0;
370                                 addr += 1LL << (6+6);
371                                 addr &= ~0xFFF; // (1LL << 6+6) - 1
372                                 continue;
373                         }
374                         // Check page block (64 pages)
375                         if( gaMainBitmap[addr >> 6] + 1 == 0) {
376                                 LOG("nFree = %i = 0 (main) (0x%x)", nFree, addr);
377                                 nFree = 0;
378                                 addr += 1LL << (6);
379                                 addr &= ~0x3F;
380                                 continue;
381                         }
382                         // Check individual page
383                         if( gaMainBitmap[addr >> 6] & (1LL << (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] |= 1LL << (addr & 63);
426                 rangeID = MM_int_GetRangeID(addr << 12);
427                 giPhysRangeFree[ rangeID ] --;
428                 LOG("%x == %x", addr, giPhysRangeFirst[ rangeID ]);
429                 if(addr == giPhysRangeFirst[ rangeID ])
430                         giPhysRangeFirst[ rangeID ] += 1;
431         }
432         ret = addr;     // Save the return address
433         
434         // Update super bitmap
435         Num += addr & (64-1);
436         addr &= ~(64-1);
437         Num = (Num + (64-1)) & ~(64-1);
438         for( i = 0; i < Num/64; i++ )
439         {
440                 if( gaMainBitmap[ addr >> 6 ] + 1 == 0 )
441                         gaSuperBitmap[addr>>12] |= 1LL << ((addr >> 6) & 63);
442         }
443         
444         RELEASE(&glPhysicalPages);
445         LEAVE('x', ret << 12);
446         return ret << 12;
447 }
448
449 /**
450  * \brief Allocate a single physical page, with no preference as to address
451  *        size.
452  */
453 tPAddr MM_AllocPhys(void)
454 {
455          int    i;
456         
457         // Hack to allow allocation during setup
458         for(i = 0; i < NUM_STATIC_ALLOC; i++) {
459                 if( gaiStaticAllocPages[i] ) {
460                         tPAddr  ret = gaiStaticAllocPages[i];
461                         gaiStaticAllocPages[i] = 0;
462                         Log("MM_AllocPhys: Return %x, static alloc %i", ret, i);
463                         return ret;
464                 }
465         }
466         
467         return MM_AllocPhysRange(1, -1);
468 }
469
470 /**
471  * \brief Reference a physical page
472  */
473 void MM_RefPhys(tPAddr PAddr)
474 {
475         Uint64  page = PAddr >> 12;
476         
477         if( PAddr >> 12 > giMaxPhysPage )       return ;
478         
479         if( gaMainBitmap[ page >> 6 ] & (1LL << (page&63)) )
480         {
481                 // Reference again
482                 gaMultiBitmap[ page >> 6 ] |= 1LL << (page&63);
483                 gaiPageReferences[ page ] ++;
484         }
485         else
486         {
487                 // Allocate
488                 gaMainBitmap[page >> 6] |= 1LL << (page&63);
489                 if( gaMainBitmap[page >> 6 ] + 1 == 0 )
490                         gaSuperBitmap[page>> 12] |= 1LL << ((page >> 6) & 63);
491         }
492 }
493
494 /**
495  * \brief Dereference a physical page
496  */
497 void MM_DerefPhys(tPAddr PAddr)
498 {
499         Uint64  page = PAddr >> 12;
500         
501         if( PAddr >> 12 > giMaxPhysPage )       return ;
502         
503         if( gaMultiBitmap[ page >> 6 ] & (1LL << (page&63)) ) {
504                 gaiPageReferences[ page ] --;
505                 if( gaiPageReferences[ page ] == 1 )
506                         gaMultiBitmap[ page >> 6 ] &= ~(1LL << (page&63));
507                 if( gaiPageReferences[ page ] == 0 )
508                         gaMainBitmap[ page >> 6 ] &= ~(1LL << (page&63));
509         }
510         else
511                 gaMainBitmap[ page >> 6 ] &= ~(1LL << (page&63));
512         
513         // Update the free counts if the page was freed
514         if( !(gaMainBitmap[ page >> 6 ] & (1LL << (page&63))) )
515         {
516                  int    rangeID;
517                 rangeID = MM_int_GetRangeID( PAddr );
518                 giPhysRangeFree[ rangeID ] ++;
519                 if( giPhysRangeFirst[rangeID] > page )
520                         giPhysRangeFirst[rangeID] = page;
521                 if( giPhysRangeLast[rangeID] < page )
522                         giPhysRangeLast[rangeID] = page;
523         }
524         
525         // If the bitmap entry is not -1, unset the bit in the super bitmap
526         if(gaMainBitmap[ page >> 6 ] + 1 != 0 ) {
527                 gaSuperBitmap[page >> 12] &= ~(1LL << ((page >> 6) & 63));
528         }
529 }
530
531 /**
532  * \brief Takes a physical address and returns the ID of its range
533  * \param Addr  Physical address of page
534  * \return Range ID from eMMPhys_Ranges
535  */
536 int MM_int_GetRangeID( tPAddr Addr )
537 {
538         if(Addr >> 32)
539                 return MM_PHYS_MAX;
540         else if(Addr >> 24)
541                 return MM_PHYS_32BIT;
542         else if(Addr >> 20)
543                 return MM_PHYS_24BIT;
544         else if(Addr >> 16)
545                 return MM_PHYS_20BIT;
546         else
547                 return MM_PHYS_16BIT;
548 }

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