github.com/f-secure-foundry/tamago@v0.0.0-20220307101044-d73fcdd7f11b/dma/alloc.go (about) 1 // First-fit memory allocator for DMA buffers 2 // https://github.com/f-secure-foundry/tamago 3 // 4 // Copyright (c) F-Secure Corporation 5 // https://foundry.f-secure.com 6 // 7 // Use of this source code is governed by the license 8 // that can be found in the LICENSE file. 9 10 package dma 11 12 import ( 13 "container/list" 14 "unsafe" 15 ) 16 17 func (b *block) read(off int, buf []byte) { 18 var ptr unsafe.Pointer 19 20 ptr = unsafe.Add(ptr, int(b.addr) + off) 21 mem := unsafe.Slice((*byte)(ptr), len(buf)) 22 23 copy(buf, mem) 24 } 25 26 func (b *block) write(off int, buf []byte) { 27 var ptr unsafe.Pointer 28 29 ptr = unsafe.Add(ptr, int(b.addr) + off) 30 mem := unsafe.Slice((*byte)(ptr), len(buf)) 31 32 copy(mem, buf) 33 } 34 35 func (dma *Region) defrag() { 36 var prevBlock *block 37 38 // find contiguous free blocks and combine them 39 for e := dma.freeBlocks.Front(); e != nil; e = e.Next() { 40 b := e.Value.(*block) 41 42 if prevBlock != nil { 43 if prevBlock.addr+uint32(prevBlock.size) == b.addr { 44 prevBlock.size += b.size 45 defer dma.freeBlocks.Remove(e) 46 continue 47 } 48 } 49 50 prevBlock = e.Value.(*block) 51 } 52 } 53 54 func (dma *Region) alloc(size int, align int) *block { 55 var e *list.Element 56 var freeBlock *block 57 var pad int 58 59 if align == 0 { 60 // force word alignment 61 align = 4 62 } 63 64 // find suitable block 65 for e = dma.freeBlocks.Front(); e != nil; e = e.Next() { 66 b := e.Value.(*block) 67 68 // pad to required alignment 69 pad = -int(b.addr) & (align - 1) 70 size += pad 71 72 if b.size >= size { 73 freeBlock = b 74 break 75 } 76 } 77 78 if freeBlock == nil { 79 panic("out of memory") 80 } 81 82 // allocate block from free linked list 83 defer dma.freeBlocks.Remove(e) 84 85 // adjust block to desired size, add new block for remainder 86 if r := freeBlock.size - size; r != 0 { 87 newBlockAfter := &block{ 88 addr: freeBlock.addr + uint32(size), 89 size: r, 90 } 91 92 freeBlock.size = size 93 dma.freeBlocks.InsertAfter(newBlockAfter, e) 94 } 95 96 if pad != 0 { 97 // claim padding space 98 newBlockBefore := &block{ 99 addr: freeBlock.addr, 100 size: pad, 101 } 102 103 freeBlock.addr += uint32(pad) 104 freeBlock.size -= pad 105 dma.freeBlocks.InsertBefore(newBlockBefore, e) 106 } 107 108 return freeBlock 109 } 110 111 func (dma *Region) free(usedBlock *block) { 112 for e := dma.freeBlocks.Front(); e != nil; e = e.Next() { 113 b := e.Value.(*block) 114 115 if b.addr > usedBlock.addr { 116 dma.freeBlocks.InsertBefore(usedBlock, e) 117 dma.defrag() 118 return 119 } 120 } 121 122 dma.freeBlocks.PushBack(usedBlock) 123 }