1-- vim:set ft=lua:
2local require = require
3local pairs = pairs
4local setmetatable = setmetatable
5local getmetatable = getmetatable
6local error = error
7local type = type
8
9local _ENV = require "L4"
10local string = require "string"
11local table = require "table"
12
13-- Add this alias, it sounds better for some cases
14Env.user_factory = Env.mem_alloc;
15
16-- L4 protocol constants
17Proto = {
18  Dataspace = 0x4000,
19  Namespace = 0x4001,
20  Goos      = 0x4003,
21  Mem_alloc = 0x4004,
22  Rm        = 0x4005,
23  Event     = 0x4006,
24  Inhibitor = 0x4007,
25  Sigma0    = -6,
26  Log       = -13,
27  Scheduler = -14,
28  Factory   = -15,
29  Vm         = -16,
30  Dma_space  = -17,
31  Irq_sender = -18,
32  Semaphore  = -20,
33  Iommu      = -22,
34  Ipc_gate  = 0,
35}
36
37-- L4 rights flags
38Rights = {
39  s   = 2,
40  w   = 1,
41  r   = 4,
42  d   = 8,
43  n   = 16,
44  c   = 32,
45  ro  = 4,
46  rw  = 5,
47  rws = 7,
48}
49
50-- Ldr flags
51Ldr_flags = {
52  eager_map    = 0x1, -- L4RE_AUX_LDR_FLAG_EAGER_MAP
53  all_segs_cow = 0x2, -- L4RE_AUX_LDR_FLAG_ALL_SEGS_COW
54  pinned_segs  = 0x4, -- L4RE_AUX_LDR_FLAG_PINNED_SEGS
55}
56
57-- Flags for dataspace allocation via user_factory
58-- NOTE: keep constants in sync with l4re/include/mem_alloc
59Mem_alloc_flags = {
60  Continuous  = 1,
61  Pinned      = 2,
62  Super_pages = 4,
63}
64
65-- L4Re debug constants
66Dbg = {
67  Info       = 1,
68  Warn       = 2,
69  Boot       = 4,
70  Server     = 0x10,
71  Exceptions = 0x20,
72  Cmd_line   = 0x40,
73  Loader     = 0x80,
74  Name_space = 0x400,
75  All        = 0xffffffff,
76}
77
78-- Loader class, encapsulates a loader instance.
79--  * A memory allocator
80--  * A factory used for name-space creation (ns_fab)
81--  * A factory used for region-map creation (rm_fab)
82--  * A factory used for log creation (log_fab)
83
84Loader = {};
85Loader.__index = Loader;
86
87Class = {};
88
89function Class.check(obj, class)
90  if not obj or getmetatable(obj) ~= class then
91    error("method called with incompatible object", 3);
92  end
93end
94
95function Loader.new(proto)
96  local f = proto or {};
97
98  do
99    local lfab = f.loader or f.mem;
100    f.log_fab = f.log_fab or lfab;
101    f.ns_fab = f.ns_fab or lfab;
102    f.rm_fab = f.rm_fab or lfab;
103    f.factory = f.factory or Env.factory;
104  end
105
106  setmetatable(f, Loader);
107  return f;
108end
109
110function mangle_class(n)
111  local m = "N";
112  for i in string.gmatch(n, "([^:%s]+)") do
113    m = m .. #i .. i;
114  end
115  return m .. "E";
116end
117
118function cast(to, cap)
119  if type(to) == 'number' then
120    return __cast(to, cap)
121  elseif type(to) == 'string' then
122    return __cast(mangle_class(to), cap)
123  end
124  return nil
125end
126
127function get_cap_class(id)
128  local t = type(id);
129  if t == "number" then
130    return _CAP_TYPES[id];
131  elseif t == "string" then
132    return _CAP_TYPES[mangle_class(id)];
133  else
134    return nil;
135  end
136end
137
138local ns_class = get_cap_class("L4Re::Namespace");
139if ns_class then
140  ns_class.register = function (self, key, value, fab)
141    if type(value) == "string" then
142      local res = Env
143      for i in string.gmatch(value, "([^/]+)") do
144        if type(res) == "table" then
145          res = res[i]
146        elseif res then
147          res = res:query(i)()
148        else
149          break
150        end
151      end
152      if res == nil then
153        error("Could not resolve: '" .. value .. "'", 5)
154      end
155      value = res
156    end
157
158    if type(value) == "function" then
159      value = value(self, key);
160    end
161
162    if value ~= nil then
163      if type(value) ~= "table" then
164        self:__register(key, value);
165      elseif (fab) then
166        self:__register(key, fab(value));
167      end
168    end
169  end
170  ns_class.r = ns_class.register;
171else
172  error("Could not find type information for L4Re::Namespace");
173end
174
175ns_class = nil;
176
177function Loader:fill_namespace(ns, tmpl, fab)
178  local function cns(value)
179    return self:create_namespace(value, fab);
180  end
181
182  for k, v in pairs(tmpl) do
183    ns:r(k, v, cns);
184  end
185end
186
187
188function Loader:create_namespace(n, fab)
189  Class.check(self, Loader);
190
191  if type(n) ~= "table" then
192    return n;
193  end
194
195  local ns_fab = fab or self.ns_fab;
196  local ns = ns_fab:create(Proto.Namespace);
197  self:fill_namespace(ns, n, ns_fab);
198  return ns;
199end
200
201
202
203App_env = {}
204App_env.__index = App_env;
205
206function App_env.new(proto)
207  local f = proto or {};
208
209  f.loader = f.loader or default_loader;
210  f.rm_fab = f.rm_fab or f.loader.rm_fab;
211  f.factory = f.factory or f.loader.factory or Env.factory;
212  --  f.scheduler = f.scheduler or f.loader.scheduler;
213
214  f.mem = f.mem or f.loader.mem;
215
216  if type(f.log) == "table" then
217    f.log_args = f.log;
218    f.log = nil;
219  elseif type(f.log) == "function" then
220    f.log = f.log()
221    f.log_args = {}
222  else
223    f.log_args = {}
224  end
225
226  setmetatable(f, App_env);
227
228  if type(f.ns) == "table" then
229    f.ns = f.loader:create_namespace(f.ns, f.ns_fab);
230  end
231
232  return f;
233end
234
235function App_env:log()
236  Class.check(self, App_env);
237  if self.loader.log_fab == nil or self.loader.log_fab.create == nil then
238    error ("Starting an application without valid log factory", 4);
239  end
240  return self.loader.log_fab:create(Proto.Log, table.unpack(self.log_args));
241end
242
243function App_env:start(...)
244  Class.check(self, App_env);
245
246  local function fa(a)
247    return string.gsub(a, ".*/", "");
248  end
249  local old_log_tag = self.log_args[1];
250  self.log_args[1] = self.log_args[1] or fa(...);
251  local res = exec(self, ...);
252  self.log_args[1] = old_log_tag;
253  return res;
254end
255
256function App_env:set_ns(tmpl)
257  Class.check(self, App_env);
258  self.ns = Namespace.new(tmpl, self.ns_fab);
259end
260
261function App_env:set_loader_fab(fab)
262  Class.check(self, App_env);
263  self.log_fab = fab;
264  self.ns_fab = fab;
265  self.rm_fab = fab;
266end
267
268function App_env:set_mem_alloc(mem)
269  Class.check(self, App_env);
270  self.mem = mem;
271end
272
273function Loader:startv(env, ...)
274  Class.check(self, Loader);
275
276  local caps = env.caps or {};
277
278  if (type(caps) == "table") then
279    for k, v in pairs(caps) do
280      if type(v) == "table" and getmetatable(v) == nil then
281        caps[k] = self:create_namespace(v)
282      end
283    end
284
285    local defcaps = self.default_caps or { rom = Env.rom:m("r") }
286
287    for k, v in pairs(defcaps) do
288      caps[k] = caps[k] or v
289    end
290  end
291
292  env.loader = self;
293  env.caps = caps;
294  env.l4re_dbg = env.l4re_dbg or Dbg.Warn;
295  local e = App_env.new(env);
296  return e:start(...);
297end
298
299-- Create a new IPC gate for a client-server connection
300function Loader:new_channel()
301  return self.factory:create(Proto.Ipc_gate);
302end
303
304function Loader.split_args(cmd, posix_env)
305  local a = {};
306  local i = 1;
307  for w in string.gmatch(cmd, "[^%s]+") do
308    a[i] = w;
309    i = i + 1;
310  end
311  a[i] = posix_env;
312  return table.unpack(a);
313end
314
315function Loader:start(env, cmd, posix_env)
316  Class.check(self, Loader);
317  return self:startv(env, self.split_args(cmd, posix_env));
318end
319
320default_loader = Loader.new({factory = Env.factory, mem = Env.mem_alloc});
321
322return _ENV
323