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

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