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

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