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

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