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

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