summary refs log tree commit diff stats
path: root/lib/genode/alloc.nim
blob: 52dc1c32cc44bb0231988387d24ee73fe999a7ff (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#
#
#            Nim's Runtime Library
#        (c) Copyright 2017 Emery Hemingway
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

# Low level dataspace allocator for Genode.
# For interacting with dataspaces outside of the
# standard library see the Genode Nimble package.

when not defined(genode):
  {.error: "Genode only module".}

when not declared(GenodeEnv):
  include genode/env

type DataspaceCapability {.
  importcpp: "Genode::Dataspace_capability", pure.} = object

type
  Map = object
    attachment: pointer
    size: int
    ds: DataspaceCapability

  SlabMeta = object
    next: ptr MapSlab
    ds: DataspaceCapability

  MapSlab = object
    meta: SlabMeta
    maps: array[1,Map]

const SlabBackendSize = 4096

proc ramAvail(env: GenodeEnv): int {.
  importcpp: "#->pd().avail_ram().value".}
  ## Return number of bytes available for allocation.

proc capsAvail(env: GenodeEnv): int {.
  importcpp: "#->pd().avail_caps().value".}
  ## Return the number of available capabilities.
  ## Each dataspace allocation consumes a capability.

proc allocDataspace(env: GenodeEnv; size: int): DataspaceCapability {.
  importcpp: "#->pd().alloc(@)".}
  ## Allocate a dataspace and its capability.

proc attachDataspace(env: GenodeEnv; ds: DataspaceCapability): pointer {.
  importcpp: "#->rm().attach(@)".}
  ## Attach a dataspace into the component address-space.

proc detachAddress(env: GenodeEnv; p: pointer) {.
  importcpp: "#->rm().detach(@)".}
  ## Detach a dataspace from the component address-space.

proc freeDataspace(env: GenodeEnv; ds: DataspaceCapability) {.
  importcpp: "#->pd().free(@)".}
  ## Free a dataspace.

proc newMapSlab(): ptr MapSlab =
  let
    ds = runtimeEnv.allocDataspace SlabBackendSize
    p = runtimeEnv.attachDataspace ds
  result = cast[ptr MapSlab](p)
  result.meta.ds = ds

iterator items(s: ptr MapSlab): ptr Map =
  let mapCount = (SlabBackendSize - sizeof(SlabMeta)) div sizeof(Map)
  for i in 0 .. <mapCount:
    yield s.maps[i].addr

var slabs: ptr MapSlab

proc osAllocPages(size: int): pointer =
  if slabs.isNil:
    slabs = newMapSlab()
  var
    slab = slabs
    map: ptr Map
  let mapCount = (SlabBackendSize - sizeof(SlabMeta)) div sizeof(Map)
  block findFreeMap:
    while true:
      # lookup first free spot in slabs
      for m in slab.items:
        if m.attachment.isNil:
          map = m
          break findFreeMap
      if slab.meta.next.isNil:
        slab.meta.next = newMapSlab()
          # tack a new slab on the tail
      slab = slab.meta.next
        # move to next slab in linked list
  map.ds = runtimeEnv.allocDataspace size
  map.size = size
  map.attachment = runtimeEnv.attachDataspace map.ds
  result = map.attachment

proc osTryAllocPages(size: int): pointer =
  if runtimeEnv.ramAvail() >= size and runtimeEnv.capsAvail() > 4:
    result = osAllocPages size

proc osDeallocPages(p: pointer; size: int) =
  var slab = slabs
  while not slab.isNil:
    # lookup first free spot in slabs
    for m in slab.items:
      if m.attachment == p:
        if m.size != size:
          echo "cannot partially detach dataspace"
          quit -1
        runtimeEnv.detachAddress m.attachment
        runtimeEnv.freeDataspace m.ds
        m[] = Map()
        return
    slab = slab.meta.next