4 * Physical Memory Manager
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
21 extern void gKernelEnd;
24 tSpinlock glPhysicalPages;
25 Uint64 *gaSuperBitmap; // 1 bit = 64 Pages
26 Uint32 *gaiPageReferences = (void*)MM_PAGE_COUNTS; // Reference Counts
27 tPAddr giFirstFreePage; // First possibly free page
28 Uint64 giPhysRangeFree[NUM_MM_PHYS_RANGES]; // Number of free pages in each range
29 Uint64 giPhysRangeFirst[NUM_MM_PHYS_RANGES]; // First free page in each range
30 Uint64 giPhysRangeLast[NUM_MM_PHYS_RANGES]; // Last free page in each range
31 Uint64 giMaxPhysPage = 0; // Maximum Physical page
34 void MM_InitPhys_Multiboot(tMBoot_Info *MBoot)
36 tMBoot_MMapEnt *mmapStart;
42 Log("MM_InitPhys_Multiboot: (MBoot=%p)", MBoot);
44 // Scan the physical memory map
45 // Looking for the top of physical memory
46 mmapStart = (void *)( KERNEL_BASE | MBoot->MMapAddr );
47 Log(" MM_InitPhys_Multiboot: mmapStart = %p", mmapStart);
49 while( (Uint)ent < (Uint)mmapStart + MBoot->MMapLength )
51 // Adjust for the size of the entry
53 Log(" MM_InitPhys_Multiboot: ent={Type:%i,Base:0x%x,Length:%x",
54 ent->Type, ent->Base, ent->Length);
56 // If entry is RAM and is above `maxAddr`, change `maxAddr`
57 if(ent->Type == 1 && ent->Base + ent->Length > maxAddr)
58 maxAddr = ent->Base + ent->Length;
61 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
64 // Did we find a valid end?
66 // No, darn, let's just use the HighMem hack
67 giMaxPhysPage = (MBoot->HighMem >> 2) + 256; // HighMem is a kByte value
70 // Goodie, goodie gumdrops
71 giMaxPhysPage = maxAddr >> 12;
73 Log(" MM_InitPhys_Multiboot: giMaxPhysPage = 0x%x", giMaxPhysPage);
75 // Find a contigous section of memory to hold it in
76 // - Starting from the end of the kernel
77 // - We also need a region for the super bitmap
78 superPages = ((giMaxPhysPage+64*8-1)/(64*8) + 0xFFF) >> 12;
79 numPages = (giMaxPhysPage + 7) * sizeof(*gaiPageReferences);
80 numPages = (numPages + 0xFFF) >> 12;
81 Log(" MM_InitPhys_Multiboot: numPages = %i", numPages);
84 // Ok, naieve allocation, just put it after the kernel
85 tVAddr vaddr = MM_PAGE_COUNTS;
86 tPAddr paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
93 // Allocate the super bitmap
94 gaSuperBitmap = (void*) MM_MapHWPages(
96 ((giMaxPhysPage+64*8-1)/(64*8) + 0xFFF) >> 12
99 // Scan for a nice range
102 int todo = numPages + superPages;
104 tVAddr vaddr = MM_PAGE_COUNTS;
108 (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
109 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
118 // Let's not put it below the kernel, shall we?
119 if( ent->Base + ent->Size < (tPAddr)&gKernelEnd )
122 // Check if the kernel is in this range
123 if( ent->Base < (tPAddr)&gKernelEnd - KERNEL_BASE && ent->Base + ent->Size > (tPAddr)&gKernelEnd )
125 avail = (ent->Size-((tPAddr)&gKernelEnd-KERNEL_BASE-ent->Base)) >> 12;
126 paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
128 // No? then we can use all of the block
131 avail = ent->Size >> 12;
139 max = todo < avail ? todo : avail;
140 for( i = 0; i < max; i ++ )
142 MM_Map(vaddr, paddr);
147 // Alter the destination address when needed
148 if(todo == superPages)
149 vaddr = MM_PAGE_SUPBMP;
157 // - initialise to one, then clear the avaliable areas
158 memset(gaSuperBitmap, -1, (giMaxPhysPage+64*8-1)/(64*8));
159 memset(gaiPageReferences, -1, giMaxPhysPage*sizeof(*gaiPageReferences));
160 // - Clear all Type=1 areas
163 (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
164 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
167 // Check if the type is RAM
168 if(ent->Type != 1) continue;
171 &gaiPageReferences[ ent->Base >> 12 ],
173 (ent->Size>>12)*sizeof(*gaiPageReferences)
177 // Reference the used pages
179 // - Reference Counts and Bitmap
183 * \brief Allocate a contiguous range of physical pages with a maximum
184 * bit size of \a Bits
185 * \param Num Number of pages to allocate
186 * \param Bits Maximum size of the physical address
187 * \note If \a Bits is <= 0, any sized address is used (with preference
188 * to higher addresses)
190 tPAddr MM_AllocPhysRange(int Num, int Bits)
196 Log("MM_AllocPhysRange: (Num=%i,Bits=%i)", Num, Bits);
198 if( Bits <= 0 ) // Speedup for the common case
199 rangeID = MM_PHYS_MAX;
201 rangeID = MM_PHYS_MAX;
203 rangeID = MM_PHYS_32BIT;
205 rangeID = MM_PHYS_24BIT;
207 rangeID = MM_PHYS_20BIT;
209 rangeID = MM_PHYS_16BIT;
211 Log(" MM_AllocPhysRange: rangeID = %i", rangeID);
213 LOCK(&glPhysicalPages);
214 Log(" MM_AllocPhysRange: i has lock");
216 // Check if the range actually has any free pages
217 while(giPhysRangeFree[rangeID] == 0 && rangeID)
220 Log(" MM_AllocPhysRange: rangeID = %i", rangeID);
222 // What the? Oh, man. No free pages
223 if(giPhysRangeFree[rangeID] == 0) {
224 RELEASE(&glPhysicalPages);
227 Warning(" MM_AllocPhysRange: Out of free pages");
229 "Out of memory (unable to fulfil request for %i pages), zero remaining",
235 // Check if there is enough in the range
236 if(giPhysRangeFree[rangeID] >= Num)
238 // Do a cheap scan, scanning upwards from the first free page in
241 addr = giPhysRangeFirst[ rangeID ];
242 while( addr < giPhysRangeLast[ rangeID ] )
244 // Check the super bitmap
245 if( gaSuperBitmap[addr >> (6+6)] == -1 ) {
248 addr &= (1 << (6+6)) - 1;
251 // Check page block (64 pages)
252 if( gaSuperBitmap[addr >> (6+6)] & (1 << (addr>>6)&63)) {
255 addr &= (1 << (12+6)) - 1;
258 // Check individual page
259 if( gaiPageReferences[addr] ) {
269 // If we don't find a contiguous block, nFree will not be equal
270 // to Num, so we set it to zero and do the expensive lookup.
271 if(nFree != Num) nFree = 0;
276 // Oops. ok, let's do an expensive check (scan down the list
277 // until a free range is found)
279 addr = giPhysRangeLast[ rangeID ];
281 RELEASE(&glPhysicalPages);
285 "Out of memory (unable to fulfil request for %i pages)",
290 Log(" MM_AllocPhysRange: nFree = %i, addr = 0x%08x", nFree, addr);
292 // Mark pages as allocated
294 for( i = 0; i < Num; i++ )
296 gaiPageReferences[addr] = 1;
298 if(addr >> 32) rangeID = MM_PHYS_MAX;
299 else if(addr >> 24) rangeID = MM_PHYS_32BIT;
300 else if(addr >> 20) rangeID = MM_PHYS_24BIT;
301 else if(addr >> 16) rangeID = MM_PHYS_20BIT;
302 else if(addr >> 0) rangeID = MM_PHYS_16BIT;
303 giPhysRangeFree[ rangeID ] --;
306 Num += addr & (64-1);
308 Num = (Num + (64-1)) & ~(64-1);
309 for( i = 0; i < Num/64; i++ )
312 for( j = 0; j < 64; j ++ ) {
313 if( gaiPageReferences[addr+i*64+j] ) {
319 gaSuperBitmap[addr>>12] |= 1 << ((addr >> 6) & 64);
322 RELEASE(&glPhysicalPages);
327 * \brief Allocate a single physical page, with no preference as to address
330 tPAddr MM_AllocPhys(void)
332 return MM_AllocPhysRange(1, -1);
336 * \brief Reference a physical page
338 void MM_RefPhys(tPAddr PAddr)
341 if( PAddr >> 12 > giMaxPhysPage ) return ;
342 gaiPageReferences[ PAddr >> 12 ] ++;
345 for( j = 0; j < 64; j++ ) {
346 if( gaiPageReferences[ PAddr >> 12 ] == 0 ) {
352 gaSuperBitmap[PAddr >> 24] |= 1 << ((PAddr >> 18) & 64);
356 * \brief Dereference a physical page
358 void MM_DerefPhys(tPAddr PAddr)
360 if( PAddr >> 12 > giMaxPhysPage ) return ;
361 gaiPageReferences[ PAddr >> 12 ] --;
362 if( gaiPageReferences[ PAddr >> 12 ] )
364 gaSuperBitmap[PAddr >> 24] &= ~(1 << ((PAddr >> 18) & 64));