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

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