Skip to content
This repository was archived by the owner on May 22, 2023. It is now read-only.

Commit 21e035b

Browse files
committed
Update readme, add example build instructions
ref #172
1 parent 8439646 commit 21e035b

File tree

1 file changed

+131
-90
lines changed

1 file changed

+131
-90
lines changed

README.md

+131-90
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,20 @@ components correctly.
1616
Gneiss is a SPARK library providing a component-based systems abstraction for
1717
trusted components. Its main design goals are portability, performance and
1818
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.
2625

2726
## Architecture
2827

2928
The library is comprised of two parts: a platform independent interface and a
3029
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.
3633

3734
Interfaces are implemented as regular or generic packages. Since components
3835
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
5855
as follows:
5956

6057
```Ada
61-
with Componolit.Gneiss.Types;
62-
with Componolit.Gneiss.Component;
58+
with Gneiss;
59+
with Gneiss.Component;
6360
6461
package Component is
6562
66-
procedure Construct (Cap : Componolit.Gneiss.Types.Capability);
63+
procedure Construct (Cap : Gneiss.Capability);
6764
procedure Destruct;
6865
69-
package Main is new Componolit.Gneiss.Component (Construct, Destruct);
66+
package Main is new Gneiss.Component (Construct, Destruct);
7067
7168
end Component;
7269
```
@@ -78,52 +75,49 @@ capability is required to initialize clients or register servers. The procedure
7875
`Destruct` is called when the platform decides to stop the component and allows
7976
it to finalize component state. As a convention the components main package is
8077
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`.
8279

8380
The simplest interfaces are used like regular libraries. An example is the
8481
`Log` client that provides standard logging facilities. A hello world with the
8582
component described above looks as follows
8683

8784
```Ada
88-
with Componolit.Gneiss.Log;
89-
with Componolit.Gneiss.Log.Client;
85+
with Gneiss.Log;
86+
with Gneiss.Log.Client;
9087
9188
package body Component is
9289
93-
Log : Componolit.Gneiss.Log.Client_Session;
90+
package Log is new Gneiss.Log;
91+
package Log_Client is new Log.Client;
9492
95-
procedure Construct (Cap : Componolit.Gneiss.Types.Capability)
93+
Client : Gneiss.Log.Client_Session;
94+
95+
procedure Construct (Cap : Gneiss.Capability)
9696
is
9797
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);
106104
else
107-
Main.Vacate (Cap, Main.Failure);
105+
Main.Vacate (Cap, Main.Failure);
108106
end if;
109107
end Construct;
110108
111109
procedure Destruct
112110
is
113111
begin
114-
if Componolit.Gneiss.Log.Initialized (Log) then
115-
Componolit.Gneiss.Log.Finalize (Log);
116-
end if;
112+
Log_Client.Finalize (Client);
117113
end Destruct;
118114
119115
end Component;
120116
```
121117

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
127121
message.
128122

129123
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
157151
specified time. The package spec is the same as in the previous example.
158152

159153
```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;
164158
165159
package body Component is
166160
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;
170164
171165
procedure Event;
172166
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);
174169
175-
procedure Construct (Cap : Componolit.Gneiss.Types.Capability)
170+
procedure Construct (Cap : Gneiss.Capability)
176171
is
177172
begin
178173
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);
185176
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)
188179
then
189-
Componolit.Gneiss.Log.Client.Info (Log, "Start!");
180+
Log_Client.Info (Log, "Start!");
190181
Timer_Client.Set_Timeout (Timer, 60.0);
191182
else
192183
Main.Vacate (Cap, Main.Failure);
@@ -197,10 +188,10 @@ package body Component is
197188
is
198189
begin
199190
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)
202193
then
203-
Componolit.Gneiss.Log.Client.Info ("60s passed!");
194+
Log_Client.Info ("60s passed!");
204195
Main.Vacate (Capability, Main.Success);
205196
else
206197
Main.Vacate (Capability, Main.Failure);
@@ -210,25 +201,19 @@ package body Component is
210201
procedure Destruct
211202
is
212203
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);
219206
end Destruct;
220207
221208
end Component;
222209
```
223210

224211
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.
232217

233218
Some interfaces need more than a simple event callback and require additional
234219
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.
244229

245230
The generic approach to implement a new platform is to create a new directory
246231
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
248233
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
250235
specific types can be anything, as they're private to all components.
251236

252237
The log client for example consists of two (in this example simplified) specs:
253238

254239
```Ada
255-
private with Componolit.Gneiss.Internal.Log;
240+
private with Gneiss_Internal.Log;
256241
257-
package Componolit.Gneiss.Log is
242+
generic
243+
package Gneiss.Log is
258244
259245
type Client_Session is limited private;
260246
261247
function Initialized (C : Client_Session) return Boolean;
262248
263249
private
264250
265-
type Client_Session is new Componolit.Gneiss.Internal.Log.Client_Session;
251+
type Client_Session is new Gneiss_Internal.Log.Client_Session;
266252
267-
end Componolit.Gneiss.Log;
253+
end Gneiss.Log;
268254
```
269255

270256
```Ada
271-
with Componolit.Gneiss.Types;
272-
273-
package Componolit.Gneiss.Log.Client is
257+
generic
258+
package Gneiss.Log.Client is
274259
275260
procedure Initialize (C : in out Client_Session;
276-
Cap : Componolit.Gneiss.Types.Capability;
261+
Cap : Capability;
277262
Label : String) with
278263
Pre => not Initialized (C);
279264
@@ -282,7 +267,7 @@ package Componolit.Gneiss.Log.Client is
282267
Pre => Initialized (C),
283268
Post => Initialized (C);
284269
285-
end Componolit.Gneiss.Log.Client;
270+
end Gneiss.Log.Client;
286271
```
287272

288273
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:
300285
```Ada
301286
with System;
302287
303-
package Componolit.Gneiss.Internal.Log is
288+
package Gneiss_Internal.Log is
304289
305290
type Client_Session is limited record
306291
Label : System.Address := System.Null_Address;
307292
end record;
308293
309-
end Componolit.Gneiss.Internal.Log;
294+
end Gneiss_Internal.Log;
310295
```
311296

312297
As the session type is limited and has no initialization operation it requires
@@ -353,10 +338,10 @@ in C.
353338
```Ada
354339
with System;
355340
356-
package body Componolit.Gneiss.Log.Client is
341+
package body Gneiss.Log.Client is
357342
358343
procedure Initialize (C : in out Client_Session;
359-
Cap : Componolit.Gneiss.Types.Capability;
344+
Cap : Capability;
360345
Label : String)
361346
is
362347
procedure C_Initialize (C_Client : in out Client_Session;
@@ -382,7 +367,7 @@ package body Componolit.Gneiss.Log.Client is
382367
C_Info (C, C_Msg'Address);
383368
end Info;
384369
385-
end Componolit.Gneiss.Log.Client;
370+
end Gneiss.Log.Client;
386371
```
387372

388373
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:
401386
```Ada
402387
with System;
403388
404-
package body Componolit.Gneiss.Log is
389+
package body Gneiss.Log is
405390
406391
use type System.Address;
407392
408393
function Initialized (C : Client_Session) return Boolean is
409394
(C.Label /= System.Null_Address);
410395
411-
end Componolit.Gneiss.Log;
396+
end Gneiss.Log;
412397
```
413398

414399
The initialization checks if `Label` is a valid address. This ensures that all
415400
procedures that have an `Initialized` precondition can safely use the label.
416401
It also makes sure that if `malloc` fails in C the session will not be
417402
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

Comments
 (0)