Merge branch 'master' of git://localhost/acess2
[tpg/acess2.git] / KernelLand / 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 <archinit.h>
9 #include <pmemmap.h>
10 #include <mm_virt.h>
11
12 #define TRACE_REF       0
13
14 enum eMMPhys_Ranges
15 {
16         MM_PHYS_16BIT,  // Does anything need this?
17         MM_PHYS_20BIT,  // Real-Mode
18         MM_PHYS_24BIT,  // ISA DMA
19         MM_PHYS_32BIT,  // x86 Hardware
20         MM_PHYS_MAX,    // Doesn't care
21         NUM_MM_PHYS_RANGES
22 };
23
24 // === IMPORTS ===
25 extern char     gKernelBase[];
26 extern char     gKernelEnd[];
27
28 // === PROTOTYPES ===
29 //void  MM_InitPhys(int NPMemRanges, tPMemMapEnt *PMemRanges);
30 //tPAddr        MM_AllocPhysRange(int Num, int Bits);
31 //tPAddr        MM_AllocPhys(void);
32 //void  MM_RefPhys(tPAddr PAddr);
33 //void  MM_DerefPhys(tPAddr PAddr);
34  int    MM_int_GetRangeID( tPAddr Addr );
35
36 // === MACROS ===
37 #define PAGE_ALLOC_TEST(__page)         (gaMainBitmap[(__page)>>6] & (1ULL << ((__page)&63)))
38 #define PAGE_ALLOC_SET(__page)          do{gaMainBitmap[(__page)>>6] |= (1ULL << ((__page)&63));}while(0)
39 #define PAGE_ALLOC_CLEAR(__page)        do{gaMainBitmap[(__page)>>6] &= ~(1ULL << ((__page)&63));}while(0)
40 //#define PAGE_MULTIREF_TEST(__page)    (gaMultiBitmap[(__page)>>6] & (1ULL << ((__page)&63)))
41 //#define PAGE_MULTIREF_SET(__page)     do{gaMultiBitmap[(__page)>>6] |= 1ULL << ((__page)&63);}while(0)
42 //#define PAGE_MULTIREF_CLEAR(__page)   do{gaMultiBitmap[(__page)>>6] &= ~(1ULL << ((__page)&63));}while(0)
43
44 // === GLOBALS ===
45 tMutex  glPhysicalPages;
46 Uint64  *gaSuperBitmap = (void*)MM_PAGE_SUPBMP; // 1 bit = 64 Pages, 16 MiB per Word
47 Uint64  *gaMainBitmap = (void*)MM_PAGE_BITMAP;  // 1 bit = 1 Page, 256 KiB per Word
48 Uint64  *gaMultiBitmap = (void*)MM_PAGE_DBLBMP; // Each bit means that the page is being used multiple times
49 Uint32  *gaiPageReferences = (void*)MM_PAGE_COUNTS;     // Reference Counts
50 void    **gapPageNodes = (void*)MM_PAGE_NODES;  // Reference Counts
51 tPAddr  giFirstFreePage;        // First possibly free page
52 Uint64  giPhysRangeFree[NUM_MM_PHYS_RANGES];    // Number of free pages in each range
53 Uint64  giPhysRangeFirst[NUM_MM_PHYS_RANGES];   // First free page in each range
54 Uint64  giPhysRangeLast[NUM_MM_PHYS_RANGES];    // Last free page in each range
55 Uint64  giMaxPhysPage = 0;      // Maximum Physical page
56 Uint64  giTotalMemorySize = 0;
57 // Only used in init, allows the init code to provide pages for use by
58 // the allocator before the bitmaps exist.
59 // 3 entries because the are three calls to MM_AllocPhys in MM_Map
60 #define NUM_STATIC_ALLOC        3
61 tPAddr  gaiStaticAllocPages[NUM_STATIC_ALLOC] = {0};
62
63 // === CODE ===
64 /**
65  * \brief Initialise the physical memory map using a Multiboot 1 map
66  */
67 void MM_InitPhys(int NPMemRanges, tPMemMapEnt *PMemRanges)
68 {
69         Uint64  maxAddr = 0;
70          int    numPages, superPages;
71          int    i;
72         Uint64  base, size;
73         tVAddr  vaddr;
74         tPAddr  paddr, firstFreePage;
75         
76         ENTER("iNPMemRanges pPMemRanges",
77                 NPMemRanges, PMemRanges);
78         
79         // Scan the physical memory map
80         // Looking for the top of physical memory
81         for( i = 0; i < NPMemRanges; i ++ )
82         {
83                 tPMemMapEnt     *ent = &PMemRanges[i];
84                 // Adjust for the size of the entry
85                 LOG("%i: ent={Type:%i,Base:0x%x,Length:%x}", i, ent->Type, ent->Start, ent->Length);
86                 
87                 // If entry is RAM and is above `maxAddr`, change `maxAddr`
88                 if(ent->Type == PMEMTYPE_FREE || ent->Type == PMEMTYPE_USED )
89                 {
90                         if( ent->Start + ent->Length > maxAddr)
91                                 maxAddr = ent->Start + ent->Length;
92                         giTotalMemorySize += ent->Length >> 12;
93                 }
94         }
95         
96         giMaxPhysPage = maxAddr >> 12;
97         LOG("giMaxPhysPage = 0x%x", giMaxPhysPage);
98
99         // Get counts of pages needed for basic structures
100         superPages = ((giMaxPhysPage+64*8-1)/(64*8) + 0xFFF) >> 12;
101         numPages = ((giMaxPhysPage+7)/8 + 0xFFF) >> 12; // bytes to hold bitmap, divided up to nearest page
102         LOG("numPages = %i, superPages = %i", numPages, superPages);
103         
104         // --- Allocate Bitmaps ---
105          int    todo = numPages*2 + superPages;
106          int    mapent = NPMemRanges-1;
107         vaddr = MM_PAGE_BITMAP;
108         paddr = -1;
109         while( todo )
110         {
111                 while(PMemRanges[mapent].Type != PMEMTYPE_FREE && mapent != -1)
112                         mapent --;
113                 if( paddr + 1 == 0 )
114                         paddr = PMemRanges[mapent].Start;
115                 if( mapent == -1 ) {
116                         // OOM During init, bad thing
117                         Log_KernelPanic("PMem", "Out of memory during init");
118                         for(;;);
119                 }
120                 
121                 // Ensure that the static allocation pool has pages
122                 for( i = 0; i < NUM_STATIC_ALLOC; i++)
123                 {
124                         if(gaiStaticAllocPages[i] == 0)
125                         {
126                                 gaiStaticAllocPages[i] = paddr;
127                                 // break to ensure we update the address correctly
128                                 break;
129                         }
130                 }
131                 
132                 if( i == NUM_STATIC_ALLOC )
133                 {
134                         // Map
135                         MM_Map(vaddr, paddr);
136                         todo --;
137                         
138                         // Update virtual pointer
139                         vaddr += 0x1000;
140                         if( todo == numPages + superPages )
141                                 vaddr = MM_PAGE_DBLBMP;
142                         if( todo == superPages )
143                                 vaddr = MM_PAGE_SUPBMP;
144                 }               
145
146                 // Update physical pointer
147                 // (underflows are detected at the top of the loop)
148                 paddr += 0x1000;
149                 if( paddr - PMemRanges[mapent].Start > PMemRanges[mapent].Length )
150                         mapent --;
151                 else {
152                         // NOTE: This hides some actually valid memory, but since the pmm
153                         // structures have an "infinite" lifetime, this is of no concequence.
154                         PMemRanges[mapent].Start += 0x1000;
155                         PMemRanges[mapent].Length -= 0x1000;
156                 }
157         }
158
159         PMemMap_DumpBlocks(PMemRanges, NPMemRanges);
160
161         // Save the current value of paddr to simplify the allocation later
162         firstFreePage = paddr;
163         
164         LOG("Clearing multi bitmap");
165         // Fill the bitmaps (set most to "allocated")
166         memset(gaMultiBitmap, 0, numPages<<12);
167         memset(gaMainBitmap,  255, numPages<<12);
168         // - Clear all Type=1 areas
169         LOG("Clearing valid regions");
170         for( i = 0; i < NPMemRanges; i ++ )
171         {
172                 tPMemMapEnt *ent = &PMemRanges[i];
173                 // Check if the type is RAM
174                 if(ent->Type != PMEMTYPE_FREE)  continue;
175                 
176                 // Main bitmap
177                 base = ent->Start >> 12;
178                 size = ent->Length >> 12;
179
180                 LOG("%i: base=%x, size=%x", i, base, size);
181                 if( base % 64 + size < 64 )
182                 {
183                         Uint64  bits = (1ULL << size) - 1;
184                         bits <<= base % 64;
185                         gaMainBitmap[base / 64] &= ~bits;
186                 }
187                 else
188                 {
189                         if(base & 63)
190                         {
191                                 // Keep lower bits
192                                 Uint64  bits = (1ULL << (base & 63)) - 1;
193                                 gaMainBitmap[base / 64] &= bits;
194                                 
195                                 size -= 64 - base % 64;
196                                 base += 64 - base % 64;
197                         }
198                         LOG("%i: base=%x, size=%x", i, base, size);
199                         memset( &gaMainBitmap[base / 64], 0, (size/64)*8 );
200                         base += size & ~(64-1);
201                         size -= size & ~(64-1);
202                         LOG("%i: base=%x, size=%x", i, base, size);
203                         if( size & 63 )
204                         {
205                                 // Unset lower bits (hence the bitwise not)
206                                 Uint64  val = (1ULL << (size & 63)) - 1;
207                                 gaMainBitmap[base / 64] &= ~val;
208                         }
209                 }
210         }
211         
212         // Free the unused static allocs
213         LOG("Freeing unused static allocations");
214         for( i = 0; i < NUM_STATIC_ALLOC; i++)
215         {
216                 if(gaiStaticAllocPages[i] == 0)
217                 {
218                         gaMainBitmap[ gaiStaticAllocPages[i] >> (12+6) ]
219                                 &= ~(1LL << ((gaiStaticAllocPages[i]>>12)&63));
220                         gaiStaticAllocPages[i] = 0;
221                 }
222         }
223         
224         // Fill the super bitmap
225         LOG("Filling super bitmap");
226         memset(gaSuperBitmap, 0, superPages<<12);
227         int nsuperbits = giMaxPhysPage / 64;    // 64 pages per bit
228         for( i = 0; i < (nsuperbits+63)/64; i ++)
229         {
230                 if( gaMainBitmap[ i ] + 1 == 0 )
231                         gaSuperBitmap[ i/64 ] |= 1ULL << (i % 64);
232         }
233         
234         // Set free page counts for each address class
235         for( base = 1; base < giMaxPhysPage; base ++ )
236         {
237                  int    rangeID;
238                 // Skip allocated
239                 if( gaMainBitmap[ base >> 6 ] & (1LL << (base&63))  )   continue;
240                 
241                 // Get range ID
242                 rangeID = MM_int_GetRangeID( base << 12 );
243                 
244                 // Increment free page count
245                 giPhysRangeFree[ rangeID ] ++;
246                 
247                 // Check for first free page in range
248                 if(giPhysRangeFirst[ rangeID ] == 0)
249                         giPhysRangeFirst[ rangeID ] = base;
250                 // Set last (when the last free page is reached, this won't be
251                 // updated anymore, hence will be correct)
252                 giPhysRangeLast[ rangeID ] = base;
253         }       
254
255         LEAVE('-');
256 }
257
258 void MM_DumpStatistics(void)
259 {
260         // TODO: Statistics for x86_64 PMM
261 }
262
263 /**
264  * \brief Allocate a contiguous range of physical pages with a maximum
265  *        bit size of \a MaxBits
266  * \param Pages Number of pages to allocate
267  * \param MaxBits       Maximum size of the physical address
268  * \note If \a MaxBits is <= 0, any sized address is used (with preference
269  *       to higher addresses)
270  */
271 tPAddr MM_AllocPhysRange(int Pages, int MaxBits)
272 {
273         tPAddr  addr, ret;
274          int    rangeID;
275          int    nFree = 0, i;
276         
277         ENTER("iPages iBits", Pages, MaxBits);
278         
279         if( MaxBits <= 0 || MaxBits >= 64 )     // Speedup for the common case
280                 rangeID = MM_PHYS_MAX;
281         else
282                 rangeID = MM_int_GetRangeID( (1LL << MaxBits) - 1 );
283         
284         LOG("rangeID = %i", rangeID);
285         
286         Mutex_Acquire(&glPhysicalPages);
287         
288         // Check if the range actually has any free pages
289         while(giPhysRangeFree[rangeID] == 0 && rangeID)
290                 rangeID --;
291         
292         LOG("rangeID = %i", rangeID);
293         
294         // What the? Oh, man. No free pages
295         if(giPhysRangeFree[rangeID] == 0) {
296                 Mutex_Release(&glPhysicalPages);
297                 // TODO: Page out
298                 // ATM. Just Warning
299                 Warning(" MM_AllocPhysRange: Out of free pages");
300                 Log_Warning("Arch",
301                         "Out of memory (unable to fulfil request for %i pages), zero remaining",
302                         Pages
303                         );
304                 LEAVE('i', 0);
305                 return 0;
306         }
307         
308         // Check if there is enough in the range
309         if(giPhysRangeFree[rangeID] >= Pages)
310         {
311                 LOG("{%i,0x%x -> 0x%x}",
312                         giPhysRangeFree[rangeID],
313                         giPhysRangeFirst[rangeID], giPhysRangeLast[rangeID]
314                         );
315                 // Do a cheap scan, scanning upwards from the first free page in
316                 // the range
317                 nFree = 0;
318                 addr = giPhysRangeFirst[ rangeID ];
319                 while( addr <= giPhysRangeLast[ rangeID ] )
320                 {
321                         //Log(" MM_AllocPhysRange: addr = 0x%x", addr);
322                         // Check the super bitmap
323                         if( gaSuperBitmap[addr >> (6+6)] + 1 == 0 ) {
324                                 LOG("nFree = %i = 0 (super) (0x%x)", nFree, addr);
325                                 nFree = 0;
326                                 addr += 1LL << (6+6);
327                                 addr &= ~0xFFF; // (1LL << 6+6) - 1
328                                 continue;
329                         }
330                         // Check page block (64 pages)
331                         if( gaMainBitmap[addr >> 6] + 1 == 0) {
332                                 LOG("nFree = %i = 0 (main) (0x%x)", nFree, addr);
333                                 nFree = 0;
334                                 addr += 1LL << (6);
335                                 addr &= ~0x3F;
336                                 continue;
337                         }
338                         // Check individual page
339                         if( gaMainBitmap[addr >> 6] & (1LL << (addr & 63)) ) {
340                                 LOG("nFree = %i = 0 (page) (0x%x)", nFree, addr);
341                                 nFree = 0;
342                                 addr ++;
343                                 continue;
344                         }
345                         nFree ++;
346                         addr ++;
347                         LOG("nFree(%i) == %i (0x%x)", nFree, Pages, addr);
348                         if(nFree == Pages)
349                                 break;
350                 }
351                 LOG("nFree = %i", nFree);
352                 // If we don't find a contiguous block, nFree will not be equal
353                 // to Num, so we set it to zero and do the expensive lookup.
354                 if(nFree != Pages)      nFree = 0;
355         }
356         
357         if( !nFree )
358         {
359                 // Oops. ok, let's do an expensive check (scan down the list
360                 // until a free range is found)
361 //              nFree = 1;
362 //              addr = giPhysRangeLast[ rangeID ];
363                 // TODO: Expensive Check
364                 Mutex_Release(&glPhysicalPages);
365                 // TODO: Page out
366                 // ATM. Just Warning
367                 Warning(" MM_AllocPhysRange: Out of memory (unable to fulfil request for %i pages)", Pages);
368                 Log_Warning("Arch",
369                         "Out of memory (unable to fulfil request for %i pages)",
370                         Pages   
371                         );
372                 LEAVE('i', 0);
373                 return 0;
374         }
375         LOG("nFree = %i, addr = 0x%08x", nFree, addr);
376         
377         // Mark pages as allocated
378         addr -= Pages;
379         for( i = 0; i < Pages; i++, addr++ )
380         {
381                 gaMainBitmap[addr >> 6] |= 1LL << (addr & 63);
382                 if( MM_GetPhysAddr( &gaiPageReferences[addr] ) )
383                         gaiPageReferences[addr] = 1;
384 //              Log("page %P refcount = %i", MM_GetRefCount(addr<<12)); 
385                 rangeID = MM_int_GetRangeID(addr << 12);
386                 giPhysRangeFree[ rangeID ] --;
387                 LOG("%x == %x", addr, giPhysRangeFirst[ rangeID ]);
388                 if(addr == giPhysRangeFirst[ rangeID ])
389                         giPhysRangeFirst[ rangeID ] += 1;
390         }
391         addr -= Pages;
392         ret = addr;     // Save the return address
393         
394         // Update super bitmap
395         Pages += addr & (64-1);
396         addr &= ~(64-1);
397         Pages = (Pages + (64-1)) & ~(64-1);
398         for( i = 0; i < Pages/64; i++ )
399         {
400                 if( gaMainBitmap[ addr >> 6 ] + 1 == 0 )
401                         gaSuperBitmap[addr>>12] |= 1LL << ((addr >> 6) & 63);
402         }
403         
404         Mutex_Release(&glPhysicalPages);
405         #if TRACE_REF
406         Log("MM_AllocPhysRange: ret = %P (Ref %i)", ret << 12, MM_GetRefCount(ret<<12));
407         #endif
408         LEAVE('x', ret << 12);
409         return ret << 12;
410 }
411
412 /**
413  * \brief Allocate a single physical page, with no preference as to address
414  *        size.
415  */
416 tPAddr MM_AllocPhys(void)
417 {
418          int    i;
419         
420         // Hack to allow allocation during setup
421         for(i = 0; i < NUM_STATIC_ALLOC; i++) {
422                 if( gaiStaticAllocPages[i] ) {
423                         tPAddr  ret = gaiStaticAllocPages[i];
424                         gaiStaticAllocPages[i] = 0;
425                         Log("MM_AllocPhys: Return %P, static alloc %i", ret, i);
426                         return ret;
427                 }
428         }
429         
430         return MM_AllocPhysRange(1, -1);
431 }
432
433 /**
434  * \brief Reference a physical page
435  */
436 void MM_RefPhys(tPAddr PAddr)
437 {
438         Uint64  page = PAddr >> 12;
439         
440         if( page > giMaxPhysPage )      return ;
441         
442         if( PAGE_ALLOC_TEST(page) )
443         {
444                 tVAddr  ref_base = ((tVAddr)&gaiPageReferences[ page ]) & ~0xFFF;
445                 // Allocate reference page
446                 if( !MM_GetPhysAddr(&gaiPageReferences[page]) )
447                 {
448                         const int       pages_per_refpage = PAGE_SIZE/sizeof(gaiPageReferences[0]);
449                          int    i;
450                          int    page_base = page / pages_per_refpage * pages_per_refpage;
451                         if( !MM_Allocate( ref_base ) ) {
452                                 Log_Error("Arch", "Out of memory when allocating reference count page");
453                                 return ;
454                         }
455                         // Fill block
456                         Log("Allocated references for %P-%P", page_base << 12, (page_base+pages_per_refpage)<<12);
457                         for( i = 0; i < pages_per_refpage; i ++ ) {
458                                  int    pg = page_base + i;
459                                 gaiPageReferences[pg] = !!PAGE_ALLOC_TEST(pg);
460                         }
461                 }
462                 gaiPageReferences[page] ++;
463         }
464         else
465         {
466                 // Allocate
467                 PAGE_ALLOC_SET(page);
468                 if( gaMainBitmap[page >> 6] + 1 == 0 )
469                         gaSuperBitmap[page>> 12] |= 1LL << ((page >> 6) & 63);
470                 if( MM_GetPhysAddr( &gaiPageReferences[page] ) )
471                         gaiPageReferences[page] = 1;
472         }
473
474         #if TRACE_REF
475         Log("MM_RefPhys: %P referenced (%i)", page << 12, MM_GetRefCount(page << 12));
476         #endif
477 }
478
479 /**
480  * \brief Dereference a physical page
481  */
482 void MM_DerefPhys(tPAddr PAddr)
483 {
484         Uint64  page = PAddr >> 12;
485         
486         if( PAddr >> 12 > giMaxPhysPage )       return ;
487         
488         if( MM_GetPhysAddr( &gaiPageReferences[page] ) )
489         {
490                 gaiPageReferences[ page ] --;
491                 if( gaiPageReferences[ page ] == 0 )
492                         PAGE_ALLOC_CLEAR(page);
493         }
494         else
495                 PAGE_ALLOC_CLEAR(page);
496         
497         // Update the free counts if the page was freed
498         if( !PAGE_ALLOC_TEST(page) )
499         {
500                  int    rangeID;
501                 rangeID = MM_int_GetRangeID( PAddr );
502                 giPhysRangeFree[ rangeID ] ++;
503                 if( giPhysRangeFirst[rangeID] > page )
504                         giPhysRangeFirst[rangeID] = page;
505                 if( giPhysRangeLast[rangeID] < page )
506                         giPhysRangeLast[rangeID] = page;
507         }
508         
509         // If the bitmap entry is not -1, unset the bit in the super bitmap
510         if(gaMainBitmap[ page >> 6 ] + 1 != 0 ) {
511                 gaSuperBitmap[page >> 12] &= ~(1LL << ((page >> 6) & 63));
512         }
513         
514         #if TRACE_REF
515         Log("Page %P dereferenced (%i)", page << 12, MM_GetRefCount(page << 12));
516         #endif
517 }
518
519 int MM_GetRefCount( tPAddr PAddr )
520 {
521         PAddr >>= 12;
522         
523         if( PAddr > giMaxPhysPage )     return 0;
524
525         if( MM_GetPhysAddr( &gaiPageReferences[PAddr] ) ) {
526                 return gaiPageReferences[PAddr];
527         }
528
529         if( PAGE_ALLOC_TEST(PAddr) )
530         {
531                 return 1;
532         }
533         return 0;
534 }
535
536 /**
537  * \brief Takes a physical address and returns the ID of its range
538  * \param Addr  Physical address of page
539  * \return Range ID from eMMPhys_Ranges
540  */
541 int MM_int_GetRangeID( tPAddr Addr )
542 {
543         if(Addr >> 32)
544                 return MM_PHYS_MAX;
545         else if(Addr >> 24)
546                 return MM_PHYS_32BIT;
547         else if(Addr >> 20)
548                 return MM_PHYS_24BIT;
549         else if(Addr >> 16)
550                 return MM_PHYS_20BIT;
551         else
552                 return MM_PHYS_16BIT;
553 }
554
555 int MM_SetPageNode(tPAddr PAddr, void *Node)
556 {
557         tPAddr  page = PAddr >> 12;
558         tVAddr  node_page = ((tVAddr)&gapPageNodes[page]) & ~(PAGE_SIZE-1);
559
560 //      if( !MM_GetRefCount(PAddr) )    return 1;
561         
562         if( !MM_GetPhysAddr((void*)node_page) ) {
563                 if( !MM_Allocate(node_page) )
564                         return -1;
565                 memset( (void*)node_page, 0, PAGE_SIZE );
566         }
567
568         gapPageNodes[page] = Node;
569         return 0;
570 }
571
572 int MM_GetPageNode(tPAddr PAddr, void **Node)
573 {
574 //      if( !MM_GetRefCount(PAddr) )    return 1;
575         PAddr >>= 12;
576         
577         if( !MM_GetPhysAddr( &gapPageNodes[PAddr] ) ) {
578                 *Node = NULL;
579                 return 0;
580         }
581
582         *Node = gapPageNodes[PAddr];
583         return 0;
584 }
585

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