Skip to content

Commit bec37f6

Browse files
ericsandurazvand
authored andcommitted
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>
1 parent bb7015c commit bec37f6

1 file changed

Lines changed: 130 additions & 23 deletions

File tree

content/guides/advanced-porting.mdx

Lines changed: 130 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -243,14 +243,14 @@ This process is usually very iterative because it requires building the unikerne
243243
Make an `include/` directory in the library's repository and copy the file:
244244

245245
```console
246-
mkdir ~/workdir/app-iperf/workdir/libs/iperf3/include
247-
cp workdir/build/libiperf3/origin/iperf-3.10.1/src/iperf_config.h ~/workdir/app-iperf/.unikraft/libs/iperf3/include
246+
$ mkdir ~/workdir/app-iperf/workdir/libs/iperf3/include
247+
$ cp workdir/build/libiperf3/origin/iperf-3.10.1/src/iperf_config.h ~/workdir/app-iperf/.unikraft/libs/iperf3/include
248248
```
249249

250250
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:
251251
Add this line in the `workdir/libs/iperf3/Makefile.uk` file:
252252

253-
```Makefile
253+
```make
254254
LIBIPERF3_CINCLUDES-y += -I$(LIBIPERF3_BASE)/include
255255
```
256256

@@ -261,8 +261,8 @@ This process is usually very iterative because it requires building the unikerne
261261
The `make` might give an error at the end, but it's fine, we can ignore it.
262262

263263
```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
266266
```
267267

268268
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
283283
However, in a later step, we'll find out that we can set some flags.
284284
If you do have flags which are immediately obvious, you set them like so in the library port's `Makefile.uk`, for example:
285285

286-
```Makefile
286+
```make
287287
LIBIPERF3_CFLAGS-y += -Wno-unused-parameter
288288
```
289289

290290
1. We have a full list of files for `iperf3` from the previous step.
291291
We can add them as known source files like so to the Unikraft port of `iperf3`'s `Makefile.uk`:
292292

293-
```Makefile
293+
```make
294294
LIBIPERF3_SRCS-y += $(LIBIPERF3_SRC)/main.c
295295
LIBIPERF3_SRCS-y += $(LIBIPERF3_SRC)/cjson.c
296296
LIBIPERF3_SRCS-y += $(LIBIPERF3_SRC)/iperf_api.c
@@ -311,8 +311,8 @@ This process is usually very iterative because it requires building the unikerne
311311
Because the application has been configured and we have fetched the contents, we can simply try running the build in the Unikraft application directory:
312312

313313
```console
314-
cd ~/workdir/app-iperf3
315-
make
314+
$ cd ~/workdir/app-iperf3
315+
$ make
316316
```
317317

318318
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
328328

329329
Preparation is done by adding Make targets to the `UK_PREPARE` variable:
330330

331-
```Makefile
331+
```make
332332
UK_PREPARE += mytarget
333333
```
334334

335335
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:
336336

337-
```Makefile
337+
```make
338338
$(LIBIPERF3_BUILD)/.patched
339339
```
340340

341341
The `prepare` step is called naturally because of this target.
342342
However, it can be called separately from `make` via:
343343

344344
```console
345-
make prepare
345+
$ make prepare
346346
```
347347

348348
The steps outlined above helped us begin the process of porting a simple application to Unikraft.
@@ -406,17 +406,17 @@ To make a patch:
406406
1. First, ensure that the remote origin code has been downloaded to the application's `build/` folder:
407407

408408
```console
409-
cd ~/workspace/apps/iperf3
410-
make fetch
409+
$ cd ~/workspace/apps/iperf3
410+
$ make fetch
411411
```
412412

413413
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`:
414414

415415
```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"
420420
```
421421

422422
This will allow us to make changes to the source files and save those differences.
@@ -428,21 +428,128 @@ To make a patch:
428428
For example, if you have made one (`1`) patch only, export it like so:
429429

430430
```console
431-
git format-patch HEAD~1
431+
$ git format-patch HEAD~1
432432
```
433433

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`.
435436

436437
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:
437438

438439
```console
439-
mkdir ~/workspace/libs/iperf3/patches
440-
mv ~/workspace/apps/iperf3/build/libiperf3/origin/iperf-3.10.1/*.patch ~/workspace/libs/iperf3/patches
440+
$ mkdir ~/workspace/libs/iperf3/patches
441+
$ mv ~/workspace/apps/iperf3/build/libiperf3/origin/iperf-3.10.1/*.patch ~/workspace/libs/iperf3/patches
441442
```
442443

443444
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`:
444445

445-
```Makefile
446+
```make
446447
# Add or edit ~/workspace/libs/iperf3/Makefile.uk
447448
LIBIPERF3_PATCHDIR = $(LIBIPERF3_BASE)/patches
448449
```
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`:
472+
473+
```make
474+
LIBYOURAPPNAME_SRCS-$(CONFIG_LIBYOURAPPNAME) += $(LIBYOURAPPNAME_BASE)/extra.ld
475+
```
476+
477+
Data can be registered in this section using macros that leverage compiler attributes like `__section` and `__used`.
478+
These macros are typically found in `uk/essentials.h`:
479+
480+
```c
481+
#define MY_REGISTER(s, f) static const struct my_structure \
482+
__section(".my_section_entry") \
483+
__my_section_var __used = \
484+
{.name = (s), \
485+
.func = (f)};
486+
```
487+
488+
Iterating through these registered entries involves using the boundary symbols defined in the linker script:
489+
490+
```c
491+
extern const struct my_structure my_section_start;
492+
extern const struct my_structure my_section_end;
493+
494+
#define for_each_entry(iter) \
495+
for (iter = &my_section_start; \
496+
iter < &my_section_end; \
497+
iter++)
498+
```
499+
500+
## Integrating with Unikraft Internal APIs
501+
502+
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

Comments
 (0)