First public contribution.
1 // Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of the License "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // x86pc\nkern\tlsf.cpp
18 #include <nktest/nkutils.h>
21 extern TLinAddr RomHeaderAddress;
22 extern TLinAddr SuperPageAddress;
25 #define BLOCK_STATUS_FREE 1u // bit set if SBlock free
26 #define BLOCK_STATUS_FINAL 2u // bit set if this is last physical SBlock
27 #define BLOCK_SIZE_MASK 0xfffffffcu
31 TUint32 size; // bits 2-31 give size, bits 0,1 are status bits
32 SBlock* predecessor; // previous SBlock in physical address order
38 SFreeBlock* next; // next SBlock in same bucket, circular list
39 SFreeBlock* prev; // previous SBlock in same bucket, circular list
45 static STlsfAllocator* Create(TAny* aBase, TUint32 aTotalSize);
46 TAny* Alloc(TUint32 aSize);
47 void Free(TAny* aCell);
48 TAny* ReAlloc(TAny* aCell, TUint32 aSize);
50 void Init(TUint32 aTotalSize);
51 void InsertFree(SFreeBlock* aB);
52 TUint32 MapSize(TUint32 aSize, TUint32* p_sli, TInt round_up);
53 SFreeBlock* FindFree(TUint32 aSize);
54 void RemoveFree(SFreeBlock* aB);
55 TUint32 ConsistencyCheck();
57 TUint32 imin_size; // minimum SBlock size
58 TUint32 itotal_size; // total size of allocated and free blocks
59 TUint32 il2min; // log2 min size
60 TUint32 infl; // number of first level lists
61 TUint32 insl; // number of second level lists
63 SFreeBlock** isll; // pointer to first second level list pointer for minimum size
64 TUint32 iflb; // first level bitmap
65 SBlock* iff; // first free address
66 TUint32 islb[1]; // second level bitmaps, one for each first level list
67 // second level lists follow, nsl pointers for each first level list
70 STlsfAllocator* TheAllocator;
71 NFastMutex AllocMutex;
73 STlsfAllocator* STlsfAllocator::Create(void* aBase, TUint32 aTotalSize)
75 STlsfAllocator* p = (STlsfAllocator*)aBase;
80 void STlsfAllocator::Init(TUint32 total_size)
87 itotal_size = total_size;
88 infl = __e32_find_ms1_32(total_size) - il2min + 1;
90 a = (TUint32)&islb[infl];
92 isll = (SFreeBlock**)a;
93 a += insl * infl * sizeof(TUint32*);
96 memset(islb, 0, total_size - sizeof(STlsfAllocator) + sizeof(TUint32));
97 iff->size = (total_size - a) | BLOCK_STATUS_FINAL;
102 // size already rounded up to multiple of min_size
103 TUint32 STlsfAllocator::MapSize(TUint32 size, TUint32* p_sli, TInt round_up)
105 TUint32 sli = size >> il2min;
106 TUint32 fli = __e32_find_ms1_32(sli);
110 TUint32 sls = (fli - il2nsl);
113 if (round_up && ((sli << sls) < sz2) )
123 SFreeBlock* STlsfAllocator::FindFree(TUint32 size)
126 TUint32 fli = MapSize(size, &sli, 1);
133 if ((iflb >> fli) & 1)
135 if ((islb[fli]>>sli)==0)
142 fli2 = __e32_find_ls1_32(iflb >> fli);
146 sli2 = __e32_find_ls1_32(islb[fli2] >> sli) + sli; // must find a 1
147 sll = &isll[(fli2 << il2nsl) | sli2];
153 if ( (islb[fli2] &= ~(1u<<sli2)) == 0 )
159 b->next->prev = b->prev;
160 b->prev->next = b->next;
162 act_sz = b->b.size & BLOCK_SIZE_MASK;
166 SBlock* nfb = (SBlock*)((TUint32)b + size);
167 nfb->size = (act_sz - size) | (b->b.size & BLOCK_STATUS_FINAL);
168 nfb->predecessor = &b->b;
169 if (!(b->b.size & BLOCK_STATUS_FINAL))
171 // if this isn't final SBlock, update predecessor of following SBlock
172 SBlock* nb = (SBlock*)((TUint32)b + act_sz);
173 nb->predecessor = nfb;
175 InsertFree((SFreeBlock*)nfb);
176 b->b.size = 0; // allocated SBlock can't be final
178 b->b.size &= BLOCK_STATUS_FINAL;
183 void STlsfAllocator::InsertFree(SFreeBlock* b)
185 TUint32 size = b->b.size & BLOCK_SIZE_MASK;
187 TUint32 fli = MapSize(size, &sli, 0);
188 SFreeBlock** sll = &isll[(fli << il2nsl) | sli];
191 SFreeBlock* first = *sll;
193 b->prev = first->prev;
194 first->prev->next = b;
201 islb[fli] |= (1u<<sli);
205 b->b.size |= BLOCK_STATUS_FREE;
208 void STlsfAllocator::RemoveFree(SFreeBlock* b)
210 TUint32 size = b->b.size & BLOCK_SIZE_MASK;
212 TUint32 fli = MapSize(size, &sli, 0);
213 SFreeBlock** sll = &isll[(fli << il2nsl) | sli];
218 b->next->prev = b->prev;
219 b->prev->next = b->next;
223 if ( (islb[fli] &= ~(1u<<sli)) == 0)
227 TAny* STlsfAllocator::Alloc(TUint32 size)
230 TUint32 msm = imin_size - 1;
232 size = (size + sizeof(SBlock) + msm) &~ msm;
240 void STlsfAllocator::Free(TAny* cell)
242 SBlock* b = ((SBlock*)cell) - 1;
243 // SFreeBlock* fb = (SFreeBlock*)b;
247 size = b->size & BLOCK_SIZE_MASK;
248 if (!(b->size & BLOCK_STATUS_FINAL))
251 SBlock* nb = (SBlock*)((TUint32)b + size);
252 TUint32 nbs = nb->size;
253 if (nbs & BLOCK_STATUS_FREE)
255 // following SBlock is free
256 RemoveFree((SFreeBlock*)nb);
257 b->size = size + (nbs &~ BLOCK_STATUS_FREE); // keeps final flag from following SBlock
258 size = b->size & BLOCK_SIZE_MASK;
259 if (!(nbs & BLOCK_STATUS_FINAL))
261 nb = (SBlock*)((TUint32)b + size);
266 if (b->predecessor && b->predecessor->size & BLOCK_STATUS_FREE)
268 // predecessor SBlock is free
269 TUint32 psz = b->predecessor->size & BLOCK_SIZE_MASK;
270 RemoveFree((SFreeBlock*)b->predecessor);
271 psz += b->size; // keeps final flag if necessary
272 b->predecessor->size = psz;
274 if (!(psz & BLOCK_STATUS_FINAL))
276 // need to adjust prev pointer of following SBlock
277 SBlock* nb = (SBlock*)((TUint32)b + psz);
281 InsertFree((SFreeBlock*)b);
284 TAny* STlsfAllocator::ReAlloc(TAny* cell, TUint32 newsize)
293 return (newsize>0) ? Alloc(newsize) : 0;
299 b = ((SBlock*)cell) - 1;
300 size = b->size & BLOCK_SIZE_MASK;
302 newsize = (newsize + sizeof(SBlock) + msm) &~ msm;
305 nb = (SBlock*)((TUint32)b + size);
306 if (nb->size & BLOCK_STATUS_FREE)
308 // following SBlock is free
309 TUint32 nbs = nb->size;
310 if (nbs + size >= newsize)
312 // we can expand in place - grab the entire free SBlock for now
313 // it will be split if necessary in the next section of code
314 RemoveFree((SFreeBlock*)nb);
315 b->size = size + (nbs &~ BLOCK_STATUS_FREE); // keeps final flag from following SBlock
316 size = b->size & BLOCK_SIZE_MASK;
317 if (!(nbs & BLOCK_STATUS_FINAL))
319 SBlock* nnb = (SBlock*)((TUint32)b + size);
320 nnb->predecessor = b;
329 // shrinking - split SBlock
330 TUint32 final = b->size & BLOCK_STATUS_FINAL;
331 SBlock* nfb = (SBlock*)((TUint32)b + newsize);
332 nfb->size = (size - newsize) | final;
333 nfb->predecessor = b;
336 // if this isn't final SBlock, update predecessor of following SBlock
337 nb = (SBlock*)((TUint32)b + size);
338 nb->predecessor = nfb;
340 b->size = newsize; // original SBlock can't be final
341 Free((SBlock*)nfb + 1);
345 // must move SBlock to expand
346 newcell = Alloc(newsize);
349 memcpy(newcell, cell, size);
355 TUint32 STlsfAllocator::ConsistencyCheck()
360 TUint32 total_user_size;
361 TUint32 total_block_size = 0;
362 TUint32 block_count = 0;
367 TUint32 total_free = 0;
371 memset(slb, 0, sizeof(slb));
372 __NK_ASSERT_DEBUG(imin_size == 16);
373 __NK_ASSERT_DEBUG(insl == 16);
374 __NK_ASSERT_DEBUG(il2min == 4);
375 __NK_ASSERT_DEBUG(il2nsl == 4);
376 __NK_ASSERT_DEBUG(infl == __e32_find_ms1_32(itotal_size) - il2min + 1);
377 a = (TUint32)&islb[infl];
379 __NK_ASSERT_DEBUG(isll == (SFreeBlock**)a);
380 a += insl * infl * sizeof(TUint32*);
381 __NK_ASSERT_DEBUG(iff == (SBlock*)a);
382 total_user_size = itotal_size - (a - (TUint32)this);
386 size = szs & BLOCK_SIZE_MASK;
387 __NK_ASSERT_DEBUG(b->predecessor == pb);
388 __NK_ASSERT_DEBUG(size > 0);
389 __NK_ASSERT_DEBUG(size <= total_user_size);
390 __NK_ASSERT_DEBUG(size == ((size >> il2min) << il2min));
391 total_block_size += size;
394 b = (SBlock*)((TUint32)b + size);
395 } while(!(szs & BLOCK_STATUS_FINAL));
396 __NK_ASSERT_DEBUG((TUint32)b == (TUint32)this + itotal_size);
397 __NK_ASSERT_DEBUG(total_block_size == total_user_size);
402 size = szs & BLOCK_SIZE_MASK;
403 if (szs & BLOCK_STATUS_FREE)
405 SFreeBlock* fb = (SFreeBlock*)b;
406 SFreeBlock* pfb = fb;
410 TInt c = (TInt)block_count;
411 TUint32 fli = __e32_find_ms1_32(size) - il2min;
412 TUint32 sli = (size >> il2min) - (1u << fli);
414 TUint32 fli2 = MapSize(size, &sli2, 0);
415 (void)sli2, (void)fli2;
417 sli >>= (fli - il2nsl);
418 __NK_ASSERT_DEBUG(fli == fli2);
419 __NK_ASSERT_DEBUG(sli == sli2);
421 slb[fli] |= (1u << sli);
422 lhi = (fli << il2nsl) | sli;
429 __NK_ASSERT_DEBUG(fb->prev == pfb);
430 __NK_ASSERT_DEBUG(fb->b.size & BLOCK_STATUS_FREE);
431 } while ((fb != (SFreeBlock*)b) && --c>=0);
432 __NK_ASSERT_DEBUG(fb == (SFreeBlock*)b);
433 __NK_ASSERT_DEBUG(lh_found);
436 b = (SBlock*)((TUint32)b + size);
437 } while(!(szs & BLOCK_STATUS_FINAL));
439 __NK_ASSERT_DEBUG(flb == iflb);
440 for (fli=0; fli<infl; ++fli)
442 __NK_ASSERT_DEBUG(slb[fli] == islb[fli]);
443 if (!(flb & (1u<<fli)))
444 __NK_ASSERT_DEBUG(slb[fli]==0);
445 for (sli=0; sli<insl; ++sli)
447 TUint32 lhi = (fli << il2nsl) | sli;
449 if (!(slb[fli] & (1u<<sli)))
450 __NK_ASSERT_DEBUG(!isll[lhi]);
459 #define EXPORT_C /* */
472 #include "kernboot.h"
475 void SetupMemoryAllocator()
477 const SSuperPageBase& sp = *(const SSuperPageBase*)SuperPageAddress;
478 const TRomHeader& romHdr = *(const TRomHeader*)RomHeaderAddress;
479 const TRomEntry* primaryEntry = (const TRomEntry*)sp.iPrimaryEntry;
480 const TRomImageHeader* primaryImageHeader = (const TRomImageHeader*)primaryEntry->iAddressLin;
481 TLinAddr stack = romHdr.iKernDataAddress + round_to_page(romHdr.iTotalSvDataSize);
482 TLinAddr heapbase = stack + round_to_page(primaryImageHeader->iStackSize);
483 TLinAddr heapsize = sp.iInitialHeapSize;
485 KPrintf("Heap base %08x size %08x", heapbase, heapsize);
486 TheAllocator = STlsfAllocator::Create((TAny*)heapbase, heapsize);
489 extern TBool InitialThreadDefined;
491 TAny* malloc(TUint32 aSize)
493 // __KTRACE_OPT(KMMU,DEBUGPRINT("malloc(%d)",aSize));
494 if (InitialThreadDefined)
495 NKern::FMWait(&AllocMutex);
496 TAny* p = TheAllocator->Alloc(aSize);
497 if (InitialThreadDefined)
498 NKern::FMSignal(&AllocMutex);
499 __KTRACE_OPT(KMMU,DEBUGPRINT("malloc(%d)->%08x",aSize,p));
503 void free(TAny* aCell)
505 __KTRACE_OPT(KMMU,DEBUGPRINT("free(%08x)",aCell));
509 SBlock* b = (SBlock*)aCell;
510 TUint32 size = b[-1].size & BLOCK_SIZE_MASK;
511 memset(aCell, 0xde, size - sizeof(SBlock));
514 NKern::FMWait(&AllocMutex);
515 TheAllocator->Free(aCell);
516 NKern::FMSignal(&AllocMutex);
519 TAny* realloc(TAny* aCell, TUint32 aSize)
521 NKern::FMWait(&AllocMutex);
522 TAny* p = TheAllocator->ReAlloc(aCell, aSize);
523 NKern::FMSignal(&AllocMutex);