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  }