You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(guides): Update advanced porting guide with chapters from usoc22 session
Adds 3 new sections to the guides and fixes currently existing linter
issues with the advanced-porting.mdx file.
Signed-off-by: Eric Sandu <eric_andrei.sandu@stud.acs.upb.ro>
Let's indicate in the `Makefile.uk` of the Unikraft library for `iperf3` that this directory exists, and should be used as a location to look for header files:
251
251
Add this line in the `workdir/libs/iperf3/Makefile.uk` file:
@@ -261,8 +261,8 @@ This process is usually very iterative because it requires building the unikerne
261
261
The `make` might give an error at the end, but it's fine, we can ignore it.
262
262
263
263
```console
264
-
cd workdir/build/libiperf3/origin/iperf-3.14/
265
-
make -n
264
+
$ cd workdir/build/libiperf3/origin/iperf-3.14/
265
+
$ make -n
266
266
```
267
267
268
268
This flag, `-n`, has just shown us what `make` will run, the full commands for `gcc` including flags.
@@ -283,14 +283,14 @@ This process is usually very iterative because it requires building the unikerne
283
283
However, in a later step, we'll find out that we can set some flags.
284
284
If you do have flags which are immediately obvious, you set them like so in the library port's `Makefile.uk`, for example:
285
285
286
-
```Makefile
286
+
```make
287
287
LIBIPERF3_CFLAGS-y += -Wno-unused-parameter
288
288
```
289
289
290
290
1. We have a full list of files for `iperf3` from the previous step.
291
291
We can add them as known source files like so to the Unikraft port of `iperf3`'s `Makefile.uk`:
292
292
293
-
```Makefile
293
+
```make
294
294
LIBIPERF3_SRCS-y += $(LIBIPERF3_SRC)/main.c
295
295
LIBIPERF3_SRCS-y += $(LIBIPERF3_SRC)/cjson.c
296
296
LIBIPERF3_SRCS-y += $(LIBIPERF3_SRC)/iperf_api.c
@@ -311,8 +311,8 @@ This process is usually very iterative because it requires building the unikerne
311
311
Because the application has been configured and we have fetched the contents, we can simply try running the build in the Unikraft application directory:
312
312
313
313
```console
314
-
cd ~/workdir/app-iperf3
315
-
make
314
+
$ cd ~/workdir/app-iperf3
315
+
$ make
316
316
```
317
317
318
318
1. (Optional) This step occurs less frequently, but is still useful to discuss in the context of porting an application to Unikraft.
@@ -328,21 +328,21 @@ This process is usually very iterative because it requires building the unikerne
328
328
329
329
Preparation is done by adding Make targets to the `UK_PREPARE` variable:
330
330
331
-
```Makefile
331
+
```make
332
332
UK_PREPARE += mytarget
333
333
```
334
334
335
335
Checking whether the library has been prepared or adding a target which requires preparation before it can be executed is as simple as checking whether the following target exists:
336
336
337
-
```Makefile
337
+
```make
338
338
$(LIBIPERF3_BUILD)/.patched
339
339
```
340
340
341
341
The `prepare` step is called naturally because of this target.
342
342
However, it can be called separately from `make` via:
343
343
344
344
```console
345
-
make prepare
345
+
$ make prepare
346
346
```
347
347
348
348
The steps outlined above helped us begin the process of porting a simple application to Unikraft.
@@ -406,17 +406,17 @@ To make a patch:
406
406
1. First, ensure that the remote origin code has been downloaded to the application's `build/` folder:
407
407
408
408
```console
409
-
cd ~/workspace/apps/iperf3
410
-
make fetch
409
+
$ cd ~/workspace/apps/iperf3
410
+
$ make fetch
411
411
```
412
412
413
413
1. Once the source files have been downloaded, turn it into a Git repository and save everything to an initial commit, in the case of `iperf3`:
414
414
415
415
```console
416
-
cd workdir/build/libiperf3/origin/iperf-3.10.1
417
-
git init
418
-
git add .
419
-
git commit -m "Initial commit"
416
+
$ cd workdir/build/libiperf3/origin/iperf-3.10.1
417
+
$ git init
418
+
$ git add .
419
+
$ git commit -m "Initial commit"
420
420
```
421
421
422
422
This will allow us to make changes to the source files and save those differences.
@@ -428,21 +428,128 @@ To make a patch:
428
428
For example, if you have made one (`1`) patch only, export it like so:
429
429
430
430
```console
431
-
git format-patch HEAD~1
431
+
$ git format-patch HEAD~1
432
432
```
433
433
434
-
This will save a new `.patch` file in the current directory; which should be the origin source files of `iperf3`.
434
+
This will save a new `.patch` file in the current directory;
435
+
which should be the origin source files of `iperf3`.
435
436
436
437
1. The next step is to create a `patches/` folder within the Unikraft port of the library and to move the new `.patch` file into this folder:
1. To register patches against Unikraft's build system such that they are applied before the compilation of all source files, simply indicate it in the library's `Makefile.uk`:
444
445
445
-
```Makefile
446
+
```make
446
447
# Add or edit ~/workspace/libs/iperf3/Makefile.uk
447
448
LIBIPERF3_PATCHDIR = $(LIBIPERF3_BASE)/patches
448
449
```
450
+
451
+
## Registering Components via ELF Sections
452
+
453
+
There are situations in which we want to add new sections in the executable file (ELF format) for our application or library.
454
+
These sections are useful for making components much easier to configure.
455
+
For example, the Unikraft virtual filesystem (`vfscore`) uses such a section to register filesystems (like `ramfs` or `9pfs`), and the scheduler uses them to register functions at build time.
456
+
457
+
To add such a section, you can define a linker script with the `.ld` extension (e.g., `extra.ld`):
458
+
459
+
```ld
460
+
SECTIONS
461
+
{
462
+
.my_section : {
463
+
PROVIDE(my_section_start = .);
464
+
KEEP (*(.my_section_entry))
465
+
PROVIDE(my_section_end = .);
466
+
}
467
+
}
468
+
INSERT AFTER .text;
469
+
```
470
+
471
+
Then, register the linker script in your `Makefile.uk`:
Some applications require direct interaction with Unikraft's internal system APIs.
503
+
For each category of internal library (e.g., memory allocators, schedulers, filesystems, network drivers), Unikraft defines an API that libraries in that category must comply with, allowing different implementations to be plugged in easily.
504
+
505
+
### The Virtual Filesystem API (vfscore)
506
+
507
+
The `vfscore` internal library provides the implementation for filesystem-related system calls.
508
+
It maps generic calls like `read` or `write` to specific implementations using function pointers defined in two primary structures:
509
+
510
+
- `struct vfsops`: Defines filesystem-level operations such as mounting.
511
+
- `struct vnops`: Defines operations on specific file nodes (vnodes).
512
+
513
+
Filesystems register themselves with `vfscore` using the `vfscore_fs_type` structure and the `UK_FS_REGISTER` macro:
514
+
515
+
```c
516
+
struct vfscore_fs_type {
517
+
const char *vs_name; /* name of file system */
518
+
int (*vs_init)(void); /* initialize routine */
519
+
struct vfsops *vs_op; /* pointer to vfs operation */
520
+
};
521
+
522
+
UK_FS_REGISTER(fs_ramfs);
523
+
```
524
+
525
+
Within `vfscore`, files are abstracted as `vnode` structures, which maintain metadata such as operations, permissions, and size.
526
+
They also include a `void *v_data` field for filesystem-specific private data.
527
+
Other key structures include `dentry` for path abstractions and `mount` for associated mount points.
528
+
529
+
To make a new filesystem configurable, its registration must also be reflected in the `Config.uk` of the `vfscore` library, allowing it to be selected as a root filesystem in `menuconfig`.
530
+
531
+
## Generic List API in Unikraft
532
+
533
+
Unikraft includes a generic doubly-linked list implementation, similar to the Linux kernel's, available via `<uk/list.h>`.
534
+
This API uses the `uk_list_head` structure, which can be embedded within any container structure:
535
+
536
+
```c
537
+
struct car {
538
+
char name[50];
539
+
struct uk_list_head list;
540
+
};
541
+
```
542
+
543
+
Common routines in this API include:
544
+
545
+
-`UK_LIST_HEAD(name)`: Declares the sentinel of a list globally.
546
+
547
+
-`UK_INIT_LIST_HEAD(struct uk_list_head *list)`: Initializes a list head dynamically.
548
+
549
+
-`uk_list_add(struct uk_list_head *new_entry, struct uk_list_head *head)`: Adds an entry.
550
+
551
+
-`uk_list_entry(ptr, type, field)`: Returns the structure containing the list element.
552
+
553
+
-`uk_list_for_each(p, head)`: Iterates over a list.
554
+
555
+
-`uk_list_for_each_safe(p, n, head)`: Iterates safely, allowing for entry deletion.
0 commit comments