---
# System prepended metadata

title: 'Platform Rearchitecting, November 21, 2022'
tags: [plat-rearch, ' platform', unikraft, plat]

---

---
title: "Platform Rearchitecting, November 21, 2022"
tags: unikraft, platform
datetime: 2022-11-21T12:00:00+02:00
location: Online, Discord (https://bit.ly/UnikraftDiscord), the `#maintainers-voice` voice channel
teams:
- maintainers
participants:
- TODO
---

## :dart: Agenda

- Plat rearchitecting design principles
- Hackathon tasks

## :closed_book: Discussions

Brainstorming / minutes: https://docs.google.com/document/d/1Kz0fWUe7pLl2kIx_CnNotY53h8vvSH8owUAd-ZNtB3I/edit

Tasks: https://github.com/orgs/unikraft/projects/36/views/1

## :wrench: Decisions and TODOs

## :evergreen_tree: Source Code Layout

```
arch/
    arm/
    x86_64/
drivers/ 
    blkdev/
    boot/
        efi/
        multiboot/
    bus/
       pci/
    console/
           ns16550/
           pl011/
    irq/
        gic/       
    lcpu/
        armv8/
        linuxu/ /* platform-specific variants (eg different TLS implementation, syscall handling) can be added here */
        xen-pv/
        x86_64/
    mmu/
        arm-mmu/
                include/
                       driver_headers.h
                       /* If necessary, it is possible to define headers that
                        * are available to the subsystem. This is useful for
                        * use-cases such as ukpaging where the implementation
                        * header defines static inlines that must be accessible to 
                        * the implementation of the API.
                        *
                        * Convention: include/<parent_lib_name>/
                        */
                       uk/bits/ukpaging/
                                       my_header.h
        x86_64_mmu/
        xen-pv/        /* Other non-native implementations also fit here. */
    netdev/
          vmxnet/
          xennet/
    virtio/ /* we allow deviation from strict structure for better code organization */
          mmio/
          pci/
          net/
             |_ README.md     // library specific documentation (to be automatically collected into unikraft docs)
             |_ tests/        // uktest
             |_ *.c 
             |_ *.ld          // needs to be specified in Makefile.uk like any other source / header
             |_ *.h           // private headers
             |_ include/uk/   // public headers
             |_ exportsyms.uk // optional (see https://unikraft.org/docs/develop/porting/#internals-of-the-unikraft-build-system)
             |_ localsyms.uk  // optional (see https://unikraft.org/docs/develop/porting/#internals-of-the-unikraft-build-system)
             |_ Config.uk
             |_ Makefule.uk
             |_ Makefile.rules
plat/
    kvm/
    linuxu/
    xen/
lib/ /* flat set of services - APIs */
    posix-fdtab/
    posix-socket/
    posix-process/
    ukboot/
    ukpaging/
        include/uk/paging.h
        paging.c
    ukbus/
    uklcpu/
    ukblkdev/
    uknetdev/
```

**TODO:**
- Verify whether existing subsystems fit proposed layout (all)

### arch/

`arch` can contain:
- Definitions provided by the architecture (registers etc).
- Architecture-specific compiler definitions.
- sizes of integer types (library-independent types: `__u16`, `__s64`, `__sz`), limits, etc.
- ABIs like int types, traps etc (eg arm64/lcpu.h).

The definitions in `arch` are always available to all libraries.

`arch` must NOT contain:
- Architecture-specific implementation. All implementation must be part of a library.

**TODO:**
- "These are always available to all libraries" are they already?
- Standardizing the ABIs (naming conventions, prefixes, document, ...)
- Decide on where do the APIs that move out of arch/ go to.

### drivers/

Hardware-centric low-level implementation. We consider all libraries under drivers to 
implement some type of device, even if that is a cpu native device such as an mmu.

These usually implement part of a higher-level API defined under `lib`. It may be 
possible to define also drivers that do not belong to an API defined under `lib`.
This is okay as we will be allowing direct access to drivers from the application.

## include/

All headers under `include/uk` must not depend on any libraries (except headers that are always available, arch, or libraries that provide "basic headers"). Because of that we don't also use libc types, instead use architecture-provided types (`__u64`, ...).

### Config.uk

1) Standarized feature flags (KConfig) - populated by selected Platform's Config.uk and/or drivers? Yes, but on a high level abstraction for items that don't depend on a specific API implementation of that feature, e.g., `HAVE_SMP` so that we know that multiple cores could be initialized and spinlocks might be necessary. COunter-example where this makes less sense is `HAVE_PCI` because interacting with PCI bus requires to talk with a specific API and this would be provided by a driver. So here you rather depend on `DRIVER_PCI_LEGACY`. Then we have one general exception with `HAVE_LIBC` which makes possible to replace libc easily - but APIs are assumed to be the same.
 
    /* probably standarized driver APIs */
    HAVE_DMA
    HAVE_PAGING
    HAVE_NATIVE
    HAVE_XENPV
    HAVE_PCIBUS
    
    /* Today already: */
    HAVE_LIBC
    HAVE_SCHED

2) Driver's Config.uk
    Dependency on features
    
    menuconfig MYDRIVER
    depends on !HAVE_X86 && HAVE_PAGING
    depends on DRIVERCPUNATIVE || DRIVERXENCPU
    depends on DRIVERPCI
    select HAVE_DMA /* announce this driver implements this feature */

3) Platform's Config.uk
    CONFIG PLATQEMU
        /* some basic drivers have to be hard-selected by platform also kick-start */
        select DRIVER_CPU_NATIVE  /* required*/
        select DRIVER_BOOT_MULTIBOOT1 /* image format definition */
        imply DRIVER_APIC_NATIVE if CONFIG_CPUS > 1
        imply DRIVER_PAGING_NATIVE /* Always paging? --> depends on CPU_NATIVE */
        imply DRIVERPCI
        imply DRIVERVIRTIO /* good default to have but can still be unselected */
    
    CONFIG PLATLINUXU
        select DRIVER_CPU_LINUXU
        select DRIVER_BOOT_ELF
        imply DRIVER_TAPBUS
        imply DRIVER_TAPNET

    CONFIG PLATCOQOS
        select DRIVERCPUCOQOS
        imply DRIVERMMIOBUS
    
    /* choose default set of drivers */

XX) Probably we have to differentiate between two types of drivers:

 1) Basic system drivers (where you can select only implementation, never multiple) - e.g., CPU, PAGING
    --> these might be the ones that need to be hard selected
        as starting pooint by a platform library
    --> API definition (Marc's idea) mostly consists only of headers with #if MACRO, #ifnot MACRO then function prototype -- include a standarized uk/bits/ header path. This bits header path is provided by the selected basic system driver.

 2) General drivers, that depend on specific basic drivers to be there (e.g., CPUXEN) and optionally other drivers (e.g., a bus driver like XENBUS). Example are network card drivers, disk drivers, ... -> these would be just be "imply" by platform

Decision:
- Define libraries for basic APIs like CPU etc (Simon's Basic System Drivers)
  Deprecates HAVE_FEATURE options (HAVE_ name would have been just a duplication of the API library name).
  Reasoning:
  - Makes sure that implementations do not diverge.
  - Fits well everything-is-a-library model. 
  - Allows defining overriding (alternative) APIs in external libraries that can be
    selected by implementing drivers.

New Tasks:
- Identify and define missing library APIs
- Update existing code to replace HAVE_FEATURE with library config option.

Basic drivers are only selected by the platform

SK: Still open question: things like HAVE_LIBC?
MR: Next open question: How linker scripts are built

### Access to platform-specific/driver headers from apps

**Decision:** Should be supported.

**TODO:**
- Continue the discussion on how to do that.
 
### Support multiple platforms in a single build

**Decision:** Deprecate.

### Macro definitions that are required even if a feature is not enabled (eg UK_ASSERT)

Introduce the concept of "basic macro definitions", ie definitions that are always available
in a build (`UK_ASSERT`, `uk_pr_debug()`), so that calling code won't need to wrap them around
ifdefs.

Make all headers from all libraries available to the build, regardless of whether the library
is enabled. Use guards in headers to protect definitions if the library is not enabled, or for
empty declarations like `#define UK_ASSERT`.

Example: uk/assert.h
```
#ifdef CONFIG_UKDEBUG
#define UK_ASSERT(condition) do_assert...
#else
#define UK_ASSERT(condition) do {} while(0)
#endif
```

In the Makefile change:
```
CINCLUDES-$(CONFIG_UK_DEBUG) := assert.h
```
to
```
CINCLUDES-y := assert.h
```

:warning: External libraries are allowed to provide such definitions, yet no core unikraft code
must depend on external libraries.
:warning: Definitions that are always made available (independent if library is included or not) must use basic unikraft types provided with `/arch` --> otherwise we always force a code dependency to another library (e.g., a particular libc)

## Platforms / Bootcode / Linker scripts / Image creation

Boot order:
1. boot-entry driver (U-Boot, multiboot, uefi, pcbios, mbr, ...)
2. CPU init (utilize functions provided by lcpu driver, set up runtime etc)
3. Jump back to boot-entry to prepare bootinfo structure (as defined API by ukboot)
4. Jump to generic entry point (ukboot)
5. Callback table before ctortab to do further driver initialization
6. ukboot will continue then boot procedure with ukinit (previously called ukboot). The split so that the highlevel init (scheduling, networking, application launching) can be replaced by custom ukinit implementations. ukboot is really about low-level init of hardware (irq/events/...)

- platforms consist of a single Config.uk
  - We don't use defconfig as main driving because:
      1. We would need to combine defconfig with app config
      2. An internal Config.uk provides us with more flexibility on supported features + visual menu and dependency building - we can also limit valid options for configuraiton.
      3. However, defconfigs can be still provided to pre-set some spcific platform flavors, e.g., qemu-efi, qemu-multiboot, qemu-microvm, qemu-q35.
- Move logic into drivers/ukboot that takes care of:
  - Boot-entry drivers
  - Default linker script
      - That should only contain the smallest number of sections
          - goal is to split linker script definitons as much as possible and to the library actual needing and using them so that ukcontext introduces TLS sections, ukdebug introduces debug sections, ukboot introduces ctortab and inittab, ...
          - These sections must be defined along with the required order (insert-after)
      - 
  - Default Linker.uk
- Create lcpu library with:
  - Existing lcpu functionality
  - Arch-specific cpu init code

```
lib/ukboot/
          boot.c         /* low level boot tasks (irq, events, ...) */
          Makefile.rules /* additional linker rules for image generation to avoid duplication */
          link64.h       /* header with common sections for linker scripts
                          * (minimal set of sections - libraries provide their own additional headers)
                          */
          bootinfo
          memory.c
drivers/ukboot/          /* boot-entry drivers, entry point */
              mbr/           
                  Linker.uk /* Can use rules provided by ukboot/Makefile.rules along with custom logic */
                  link64.ld.S /* Uses sections from ukboot/link64.h along with custom sections */
              multiboot/
              pcbios/
              uefi/
              u-boot/
              baremetal/ /* not applicable for x86 (doesn't make sense) - it is basically a low-level SoC format that applies to (almost) all (Arm) SoCs */
                       arm64/
              ...
lib/lcpu/                /* existing lcpu functionality + arch-specific bootstrapping */
        arm64/
             lcpu.c        /* arch-specific implementation of lcpu APIs */
             lcpu_start.S  /* arch-specific bootstrapping */
        x86_64/
              ...
        lcpu.c           /* enumeration, initialization, management, smp, ... */
        lcpu.h           /* lcpu API */

lib/ukinit/              /* high level boot: everythinig else (scheduling, networking, jump to application etc) */
          ...
```

External platform libraries then can define their own config.
Extra care must be taken to avoid changes in boot common lib breaking external platform libraries. Keep boot common minimal!

## Fine-graned platform configuraiton
Example: Allow disabling virtio, but if enabled make sure you use v2.
```
implies DRIVER_VIRTIO
select DRIVER_VIRTIO_V2
```
TODO: Investigate whether Kconfig syntax allows setting specific integer values, strings etc

## essentials.h

Split into:
1. `include/uk/compiler.h`
2. `include/uk/essentials.h`

Namespace macros (`MIN`, `MAX` etc clash with libc). Use `UK_MIN`, ...
`container_of` moves into `compiler.h`


## variable types

TODO:

GCC defines types nativel (doc?). We can use these instead of defining ours. Include these types in architecture headers, if not possible define (in the architecture) types manually.

See: https://patchwork.kernel.org/project/linux-arm-kernel/patch/1375960010-4214-1-git-send-email-ard.biesheuvel@linaro.org/

## Platform-specific topics

Platforms (eg Xen) can provide definitions (like hypcall IDs) that are required for platform-specific drivers.
Define these under the platform, and have the platform-specific drivers under `drivers/`. Simlarly for Hyper-V.

## VMM Naming

Rename KVM to QEMU
```
plat/qemu/
         system-aarch64/
                        virt/
                             Config.uk
```

## Replace internal libraries with external ones

Probably a new make variable, like `R=<name of lib to replace>:<path-to-replacement-lib>`
Ideally via `Makefile.uk` `addlib/importlib` command but how can we deal with set variables and rules from `Makefile.uk`? Maybe `importlib` could parse first instead of include?

2 stages:
 1) Build list of libraries and handle replacements
 2) Actually include the libraries from the list (`Makefile.rules`, `Makefile.uk`, ...)

## :hourglass: Pending Topics

MR:

SK:

MP:
