@@ -16,23 +16,20 @@ components correctly.
16
16
Gneiss is a SPARK library providing a component-based systems abstraction for
17
17
trusted components. Its main design goals are portability, performance and
18
18
verifiability. Components built against the library can be compiled for the
19
- [ Genode OS Framework] ( https://genode.org ) , the [ Muen Separation Kernel] ( https://muen.sk )
20
- and Linux without modification. Only a minimal runtime such as our
21
- [ ada-runtime] ( https://github.com/Componolit/ada-runtime ) is required. To enable
22
- high-performance implementations, Gneiss imposes a fully asynchronous
23
- programming style where all work is done inside event handlers. All language
24
- constructs used in Gneiss can be analyzed by the SPARK proof tools to
25
- facilitate formally verified components.
19
+ [ Genode OS Framework] ( https://genode.org ) and Linux without modification.
20
+ Only a minimal runtime such as our [ ada-runtime] ( https://github.com/Componolit/ada-runtime )
21
+ is required. To enable high-performance implementations,
22
+ Gneiss imposes a fully asynchronous programming style where all work
23
+ is done inside event handlers. All language constructs used in Gneiss can
24
+ be analyzed by the SPARK proof tools to facilitate formally verified components.
26
25
27
26
## Architecture
28
27
29
28
The library is comprised of two parts: a platform independent interface and a
30
29
platform implementation. The interface consists mostly of specifications that
31
- define the API and the SPARK contracts. It also contains utilities such as a
32
- custom ` Image ` function since this is not part of the runtime. The platform
33
- implementation is required to implement those specs with platform specific
34
- mechanisms. This can either be a native Ada platform or a binding to a
35
- different language.
30
+ define the API and the SPARK contracts. The platform implementation is required
31
+ to implement those specs with platform specific mechanisms. This can either
32
+ be a native Ada platform or a binding to a different language.
36
33
37
34
Interfaces are implemented as regular or generic packages. Since components
38
35
built with Gneiss use an asynchronous approach most interfaces trigger
@@ -58,15 +55,15 @@ procedure. This must only be done once per component. An empty component looks
58
55
as follows:
59
56
60
57
``` Ada
61
- with Componolit. Gneiss.Types ;
62
- with Componolit. Gneiss.Component;
58
+ with Gneiss;
59
+ with Gneiss.Component;
63
60
64
61
package Component is
65
62
66
- procedure Construct (Cap : Componolit. Gneiss.Types .Capability);
63
+ procedure Construct (Cap : Gneiss.Capability);
67
64
procedure Destruct;
68
65
69
- package Main is new Componolit. Gneiss.Component (Construct, Destruct);
66
+ package Main is new Gneiss.Component (Construct, Destruct);
70
67
71
68
end Component;
72
69
```
@@ -78,52 +75,49 @@ capability is required to initialize clients or register servers. The procedure
78
75
` Destruct ` is called when the platform decides to stop the component and allows
79
76
it to finalize component state. As a convention the components main package is
80
77
always called ` Component ` and it must contain an instance of the generic
81
- package ` Componolit. Gneiss.Component` that is named ` Main ` .
78
+ package ` Gneiss.Component ` that is named ` Main ` .
82
79
83
80
The simplest interfaces are used like regular libraries. An example is the
84
81
` Log ` client that provides standard logging facilities. A hello world with the
85
82
component described above looks as follows
86
83
87
84
``` Ada
88
- with Componolit. Gneiss.Log;
89
- with Componolit. Gneiss.Log.Client;
85
+ with Gneiss.Log;
86
+ with Gneiss.Log.Client;
90
87
91
88
package body Component is
92
89
93
- Log : Componolit.Gneiss.Log.Client_Session;
90
+ package Log is new Gneiss.Log;
91
+ package Log_Client is new Log.Client;
94
92
95
- procedure Construct (Cap : Componolit.Gneiss.Types.Capability)
93
+ Client : Gneiss.Log.Client_Session;
94
+
95
+ procedure Construct (Cap : Gneiss.Capability)
96
96
is
97
97
begin
98
- if not Componolit.Gneiss.Log.Initialized (Log) then
99
- Componolit.Gneiss.Log.Client.Initialize (Log, Cap, "channel1");
100
- end if;
101
- if Componolit.Gneiss.Log.Initialized (Log) then
102
- Componolit.Gneiss.Log.Client.Info (Log, "Hello World!");
103
- Componolit.Gneiss.Log.Client.Warning (Log, "Hello World!");
104
- Componolit.Gneiss.Log.Client.Error (Log, "Hello World!");
105
- Main.Vacate (Cap, Main.Success);
98
+ Log_Client.Initialize (Client, Cap, "channel1");
99
+ if Log.Initialized (Client) then
100
+ Log_Client.Info (Client, "Hello World!");
101
+ Log_Client.Warning (Client, "Hello World!");
102
+ Log_Client.Error (Client, "Hello World!");
103
+ Main.Vacate (Cap, Main.Success);
106
104
else
107
- Main.Vacate (Cap, Main.Failure);
105
+ Main.Vacate (Cap, Main.Failure);
108
106
end if;
109
107
end Construct;
110
108
111
109
procedure Destruct
112
110
is
113
111
begin
114
- if Componolit.Gneiss.Log.Initialized (Log) then
115
- Componolit.Gneiss.Log.Finalize (Log);
116
- end if;
112
+ Log_Client.Finalize (Client);
117
113
end Destruct;
118
114
119
115
end Component;
120
116
```
121
117
122
- In ` Construct ` the component checks if the log session ` Log ` is already
123
- initialized and initializes it if that is not the case. Initializing a session
124
- that has already been set up could lead to crashes or memory leaks on the
125
- platform. Since the initialization can fail it checks again if it succeeded.
126
- If this is the case it prints "Hello World!" as an info, warning and error
118
+ In ` Construct ` the component initializes the log session. Since the
119
+ initialization can fail it checks again if it succeeded. If this is
120
+ the case it prints "Hello World!" as an info, warning and error
127
121
message.
128
122
129
123
The component then calls ` Vacate ` to tell the platform that it has finished its
@@ -157,36 +151,33 @@ requires a single callback procedure that is called after a previously
157
151
specified time. The package spec is the same as in the previous example.
158
152
159
153
``` Ada
160
- with Componolit. Gneiss.Log;
161
- with Componolit. Gneiss.Log.Client;
162
- with Componolit. Gneiss.Timer;
163
- with Componolit. Gneiss.Timer.Client;
154
+ with Gneiss.Log;
155
+ with Gneiss.Log.Client;
156
+ with Gneiss.Timer;
157
+ with Gneiss.Timer.Client;
164
158
165
159
package body Component is
166
160
167
- Log : Componolit. Gneiss.Log.Client_Session;
168
- Timer : Componolit. Gneiss.Timer.Client_Session;
169
- Capability : Componolit. Gneiss.Types .Capability;
161
+ Gneiss_Log : Gneiss.Log.Client_Session;
162
+ Gneiss_Timer : Gneiss.Timer.Client_Session;
163
+ Capability : Gneiss.Capability;
170
164
171
165
procedure Event;
172
166
173
- package Timer_Client is new Componolit.Gneiss.Timer.Client (Event);
167
+ package Log_Client is new Gneiss_Log.Client;
168
+ package Timer_Client is new Gneiss_Timer.Client (Event);
174
169
175
- procedure Construct (Cap : Componolit. Gneiss.Types .Capability)
170
+ procedure Construct (Cap : Gneiss.Capability)
176
171
is
177
172
begin
178
173
Capability := Cap;
179
- if not Componolit.Gneiss.Log.Initialized (Log) then
180
- Componolit.Gneiss.Log.Client.Initialize (Log, Cap, "Timer");
181
- end if;
182
- if not Componolit.Gneiss.Timer.Initialized (Timer) then
183
- Timer_Client.Initialize (Timer, Cap);
184
- end if;
174
+ Log_Client.Initialize (Log, Cap, "Timer");
175
+ Timer_Client.Initialize (Timer, Cap);
185
176
if
186
- Componolit.Gneiss.Log .Initialized (Log)
187
- and then Componolit .Timer.Initialized (Timer)
177
+ Gneiss_Log .Initialized (Log)
178
+ and then Gneiss .Timer.Initialized (Timer)
188
179
then
189
- Componolit.Gneiss.Log.Client .Info (Log, "Start!");
180
+ Log_Client .Info (Log, "Start!");
190
181
Timer_Client.Set_Timeout (Timer, 60.0);
191
182
else
192
183
Main.Vacate (Cap, Main.Failure);
@@ -197,10 +188,10 @@ package body Component is
197
188
is
198
189
begin
199
190
if
200
- Componolit.Gneiss.Log .Initialized (Log)
201
- and then Componolit.Gneiss.Timer .Initialized (Timer)
191
+ Gneiss_Log .Initialized (Log)
192
+ and then Gneiss_Timer .Initialized (Timer)
202
193
then
203
- Componolit.Gneiss.Log.Client .Info ("60s passed!");
194
+ Log_Client .Info ("60s passed!");
204
195
Main.Vacate (Capability, Main.Success);
205
196
else
206
197
Main.Vacate (Capability, Main.Failure);
@@ -210,25 +201,19 @@ package body Component is
210
201
procedure Destruct
211
202
is
212
203
begin
213
- if Componolit.Gneiss.Log.Initialized (Log) then
214
- Componolit.Gneiss.Log.Client.Finalize (Log);
215
- end if;
216
- if Componolit.Gneiss.Timer.Initialized (Timer) then
217
- Timer_Client.Finalize (Timer);
218
- end if;
204
+ Componolit.Gneiss.Log.Client.Finalize (Log);
205
+ Timer_Client.Finalize (Timer);
219
206
end Destruct;
220
207
221
208
end Component;
222
209
```
223
210
224
211
The usage of the log session here is equivalent to the first example. Also the
225
- initialization of the timer session is similar. The main difference is that the
226
- ` Timer.Client ` package is a generic that needs to be instantiated with a
227
- procedure. When ` Set_Timeout ` is called on this instance a timeout is set on
228
- the platform. The platform will then call the ` Event ` procedure when this
229
- timeout triggers. Since the ` Event ` procedure has no arguments and can be used
230
- in multiple contexts no preconditions can be set. This requires an
231
- initialization check of the timer session.
212
+ initialization of the timer session is similar. When ` Set_Timeout ` is called
213
+ on the timer a timeout is set on the platform. The platform will then call
214
+ the ` Event ` procedure when this timeout triggers. Since the ` Event ` procedure
215
+ has no arguments and can be used in multiple contexts no preconditions can be
216
+ set. This requires an initialization check of the timer session.
232
217
233
218
Some interfaces need more than a simple event callback and require additional
234
219
interface specific procedures or functions as generic arguments. These more
@@ -244,36 +229,36 @@ object which then must be kept somewhere to be used in other contexts.
244
229
245
230
The generic approach to implement a new platform is to create a new directory
246
231
in ` platform ` and provide bodies for all specs in the ` src ` directory. Some of
247
- those specs have private parts that include ` Internal ` packages and rename
232
+ those specs have private parts that include ` Gneiss_Internal ` packages and rename
248
233
their types. Those are platform-specific types and their declaration together
249
- with the according ` Internal ` package spec need to be provided. Platform
234
+ with the according ` Gneiss_Internal ` package spec need to be provided. Platform
250
235
specific types can be anything, as they're private to all components.
251
236
252
237
The log client for example consists of two (in this example simplified) specs:
253
238
254
239
``` Ada
255
- private with Componolit.Gneiss.Internal .Log;
240
+ private with Gneiss_Internal .Log;
256
241
257
- package Componolit.Gneiss.Log is
242
+ generic
243
+ package Gneiss.Log is
258
244
259
245
type Client_Session is limited private;
260
246
261
247
function Initialized (C : Client_Session) return Boolean;
262
248
263
249
private
264
250
265
- type Client_Session is new Componolit.Gneiss.Internal .Log.Client_Session;
251
+ type Client_Session is new Gneiss_Internal .Log.Client_Session;
266
252
267
- end Componolit. Gneiss.Log;
253
+ end Gneiss.Log;
268
254
```
269
255
270
256
``` Ada
271
- with Componolit.Gneiss.Types;
272
-
273
- package Componolit.Gneiss.Log.Client is
257
+ generic
258
+ package Gneiss.Log.Client is
274
259
275
260
procedure Initialize (C : in out Client_Session;
276
- Cap : Componolit.Gneiss.Types. Capability;
261
+ Cap : Capability;
277
262
Label : String) with
278
263
Pre => not Initialized (C);
279
264
@@ -282,7 +267,7 @@ package Componolit.Gneiss.Log.Client is
282
267
Pre => Initialized (C),
283
268
Post => Initialized (C);
284
269
285
- end Componolit. Gneiss.Log.Client;
270
+ end Gneiss.Log.Client;
286
271
```
287
272
288
273
The client session is a limited private type that can neither be assigned nor
@@ -300,13 +285,13 @@ a pointer to the actual string object:
300
285
``` Ada
301
286
with System;
302
287
303
- package Componolit.Gneiss.Internal .Log is
288
+ package Gneiss_Internal .Log is
304
289
305
290
type Client_Session is limited record
306
291
Label : System.Address := System.Null_Address;
307
292
end record;
308
293
309
- end Componolit.Gneiss.Internal .Log;
294
+ end Gneiss_Internal .Log;
310
295
```
311
296
312
297
As the session type is limited and has no initialization operation it requires
@@ -353,10 +338,10 @@ in C.
353
338
```Ada
354
339
with System;
355
340
356
- package body Componolit. Gneiss.Log.Client is
341
+ package body Gneiss.Log.Client is
357
342
358
343
procedure Initialize (C : in out Client_Session;
359
- Cap : Componolit.Gneiss.Types. Capability;
344
+ Cap : Capability;
360
345
Label : String)
361
346
is
362
347
procedure C_Initialize (C_Client : in out Client_Session;
@@ -382,7 +367,7 @@ package body Componolit.Gneiss.Log.Client is
382
367
C_Info (C, C_Msg'Address);
383
368
end Info;
384
369
385
- end Componolit. Gneiss.Log.Client;
370
+ end Gneiss.Log.Client;
386
371
```
387
372
388
373
In the package body the C functions are imported. The ` Client_Session ` can be
@@ -401,17 +386,73 @@ fixed it needs to be a expression function to get into the proof context:
401
386
``` Ada
402
387
with System;
403
388
404
- package body Componolit. Gneiss.Log is
389
+ package body Gneiss.Log is
405
390
406
391
use type System.Address;
407
392
408
393
function Initialized (C : Client_Session) return Boolean is
409
394
(C.Label /= System.Null_Address);
410
395
411
- end Componolit. Gneiss.Log;
396
+ end Gneiss.Log;
412
397
```
413
398
414
399
The initialization checks if ` Label ` is a valid address. This ensures that all
415
400
procedures that have an ` Initialized ` precondition can safely use the label.
416
401
It also makes sure that if ` malloc ` fails in C the session will not be
417
402
initialized.
403
+
404
+ ## Buildsystem
405
+
406
+ Gneiss aims to integrate into the existing build systems of the supported platforms.
407
+ On Genode Gneiss components can be built with the native build system. On Linux there
408
+ is no build system that allows defining and building systems.
409
+
410
+ ### Cement
411
+
412
+ The Cement build system allows designing and building Gneiss systems that run on Linux either
413
+ in a GNU userspace or directly on the kernel. A system consists of a core component that
414
+ is executed with a configuration. The configuration declares the components and their communication
415
+ channels. A component is compiled into a shared object that is loaded by the core component and
416
+ then forks into its own process.
417
+
418
+ The build configuration is done in XML. The example for a hello world system looks as follows:
419
+
420
+ ``` XML
421
+ <config >
422
+ <component name =" log_server" file =" libcomponent_linux_log_server.so" />
423
+ <component name =" hello_world" file =" libcomponent_hello_world.so" >
424
+ <service name =" Log" server =" log_server" />
425
+ </component >
426
+ </config >
427
+ ```
428
+
429
+ The ` hello_world ` component that is implemented by ` libcomponent_hello_world.so ` is allowed to
430
+ communicate via a ` Log ` session to the ` log_server ` which then prints its outputs to the
431
+ terminal. To compile this system ` cement ` can be called with
432
+
433
+ ```
434
+ $ cd /path/to/gneiss
435
+ $ ./cement build -b build test/hello_world/hello_world.xml . test init lib
436
+ ```
437
+
438
+ Core is built in ` build/bin ` and the components are built in ` build/lib ` .
439
+ To run the system add the components that are shared libraries to the
440
+ preload path and run core with the build configuration.
441
+
442
+ ```
443
+ $ export LD_LIBRARY_PATH=build/lib
444
+ $ ./build/bin/core test/hello_world/hello_world.xml
445
+ ```
446
+
447
+ The resulting output will be:
448
+
449
+ ```
450
+ I: Loading config from test/hello_world/hello_world.xml
451
+ I: Started log_server with PID 19294
452
+ I: Started hello_world with PID 19295
453
+ [hello_world:log_hello_world] Info: Hello World!
454
+ [hello_world:log_hello_world] Warning: Hello World!
455
+ [hello_world:log_hello_world] Error: Hello World!
456
+ [hello_world:log_hello_world] Info: Destructing...
457
+ I: Component hello_world exited with status 0
458
+ ```
0 commit comments