Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions doc/source/architecture.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ filesystem, IPC, networking — plus a device-tree-driven device and driver mode
Source tree
===========

The kernel source lives under ``so3/`` and is organised by subsystem::
The kernel source lives under ``so3/so3/`` and is organised by subsystem::

so3/
so3/so3/
├── arch/ # architecture-specific code (arm32, arm64)
│ └── arm64/ # head/boot, exceptions, MMU, context switch, traps
├── kernel/ # processes, threads, scheduler, syscalls, time
Expand All @@ -35,12 +35,19 @@ The kernel source lives under ``so3/`` and is organised by subsystem::
├── dts/ # device trees (*.dts → *.dtb)
├── avz/ # the AVZ hypervisor (built with CONFIG_AVZ)
├── soo/ # the SOO framework / SO3 capsules (CONFIG_SOO)
├── apps/ # optional kernel-space example apps (CONFIG_APP_*)
├── include/ # kernel headers
├── configs/ # defconfig files
└── lib/ # in-kernel helper libraries (libfdt, libroxml, …)

The user space lives under ``usr/`` and the surrounding tooling (bootloader,
emulator, root filesystem, deployment scripts) at the repository root — see
:ref:`build_system` and :ref:`user_space`.
.. note::

Kernel paths are quoted **relative to that tree** throughout this
documentation: ``arch/arm64/mmu.c`` means ``so3/so3/arch/arm64/mmu.c``.

The user space lives under ``so3/usr/`` and the surrounding tooling (build
system, bootloader, emulator, root filesystem, deployment scripts) at the
repository root — see :ref:`build_system` and :ref:`user_space`.

Exception levels
================
Expand Down
18 changes: 13 additions & 5 deletions doc/source/avz.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ beside it.

AVZ: domains isolated by stage-2 tables, and the EL2 services beneath them.

The code lives under ``so3/avz/`` (kernel, memory, scheduler, hypercalls, grant
The code lives under ``avz/`` (kernel, memory, scheduler, hypercalls, grant
tables, capsule build/inject) together with the EL2-specific parts of
``arch/arm64`` (``head.S`` MMU setup, ``exception.S`` EL2 vectors,
``context.S`` stage-2 switch, ``cache.S`` EL2 TLB ops) and the virtual GIC in
Expand Down Expand Up @@ -92,15 +92,23 @@ Hypercalls

Guests call into AVZ with the ``hvc`` instruction, which traps to the EL2
synchronous handler (``el12_sync_handler`` in ``arch/arm64/exception.S``) and is
dispatched by ``avz/kernel/hypercalls.c``. The generic hypercalls
(``avz/include/avz/uapi/avz.h``) are:
dispatched by ``avz/kernel/hypercalls.c``. Every hypercall is one ``cmd`` value
in that single dispatcher. Three are always compiled in
(``avz/include/avz/uapi/avz.h``):

* ``AVZ_EVENT_CHANNEL_OP`` — allocate / bind / send / close event channels;
* ``AVZ_CONSOLE_IO_OP`` — console output for guests;
* ``AVZ_DOMAIN_CONTROL_OP`` — domain control (pause / unpause a capsule, …).

The capsule-management operations (inject, kill, read/write snapshot) used by the
SOO framework are built on top of these — see :ref:`capsules`.
The rest are the SOO commands, declared in ``soo/include/soo/uapi/soo.h`` and
compiled in only with ``CONFIG_SOO``: the **grant-table** op
(``AVZ_GRANT_TABLE_OP``), domain description (``AVZ_GET_DOM_DESC``), capsule
lifecycle (``AVZ_INJECT_CAPSULE``, ``AVZ_START_CAPSULE``, ``AVZ_KILL_S3C``,
``AVZ_GET_S3C_STATE`` / ``AVZ_SET_S3C_STATE``), the snapshot primitives
(``AVZ_S3C_READ_SNAPSHOT`` / ``AVZ_S3C_WRITE_SNAPSHOT``), the direct-communication
events (``AVZ_DC_EVENT_SET``) and the virtual-framebuffer ops
(``AVZ_FBDEV_*``). They are *not* layered on top of the three generic ones — see
:ref:`capsules`.

Domain scheduling
=================
Expand Down
25 changes: 16 additions & 9 deletions doc/source/build_system.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,19 @@ Meta-layers
- base bitbake classes — notably ``patch.bbclass`` (the fetch/patch/``updiff``
machinery) and the privileged-helper plumbing.
* - ``meta-so3``
- the **SO3 kernel** recipe (``so3_6.2.0.bb``, built in tree) and the **AVZ**
hypervisor recipe (``avz_6.2.0.bb``).
- the **SO3 kernel** recipe (``so3_<version>.bb``, built in tree) and the
**AVZ** hypervisor recipe (``avz_<version>.bb``); both are named after the
current release and pinned by ``PREFERRED_VERSION_*`` (see
:ref:`release_process`).
* - ``meta-usr``
- the **user space** (``usr-so3``, CMake + MUSL toolchain): a committed
lvgl-free base, plus opt-in add-ons layered as patches via overrides —
``:lvgl`` (LVGL + ``slv`` + demos) and ``:soo`` (capsule user space).
* - ``meta-bsp``
- **board support**: ``bsp-so3`` assembles the FIT image (``do_itb``) and
writes the boot media (``do_deploy_boot``).
writes the boot media (``do_deploy_boot``); ``bsp-linux`` does the same for
the Linux agency and ``bsp-capsules`` for an agency running SO3 capsules
(see :ref:`capsules`).
* - ``meta-uboot``
- the **U-Boot** bootloader (fetched + patched).
* - ``meta-qemu``
Expand Down Expand Up @@ -235,8 +239,8 @@ rebuilding.
The SO3 kernel recipe
=====================

``so3_6.2.0.bb`` configures and builds the kernel straight from ``so3/so3``; the
mechanics below are the still-familiar Kbuild ones.
``so3_<version>.bb`` configures and builds the kernel straight from ``so3/so3``;
the mechanics below are the still-familiar Kbuild ones.

Configuration (Kconfig)
-----------------------
Expand Down Expand Up @@ -303,10 +307,13 @@ paths — then assembles the ``.itb`` there with ``mkimage`` (there is no commit
* - ``<plat>_linux_guest.its``
- **Linux agency guest ITB**: Linux kernel + guest DTB + initrd, loaded by
AVZ (``meta-bsp/.../linux/files/its/``).
* - ``virt64_capsule.its``
- a capsule image
* - ``virt32_so3.its`` / ``rpi4_64_so3.its``
- the 32-bit / RPi4 standalone variants
* - ``<plat>_capsule.its``
- a capsule image (``virt64_capsule``, ``rpi4_64_capsule``)
* - ``virt32_so3.its`` / ``rpi4_64_so3.its`` / ``verdin_imx8mp_so3.its``
- the 32-bit / RPi4 / Verdin standalone variants
* - ``<plat>_lvperf.its``
- the LVGL-benchmark image driven by the ``docker/`` lvperf containers and
the CI (``virt64_lvperf``, ``virt32_lvperf`` — see :ref:`lvgl`)

``do_deploy_boot`` writes the resulting ``.itb`` from ``<ctx>/images/`` into the
FAT (boot) partition of
Expand Down
4 changes: 2 additions & 2 deletions doc/source/capsules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ The capsule model needs a **Linux** agency: Linux owns the devices and provides
the backend drivers and the higher-level services that capsules talk to — the
backend half of the frontend/backend split, the vbstore server and the
capsule-management user space (``s3c-inject``, ``s3c-list``, ``s3c-save`` /
``s3c-restore``, the EMISO engine).
``s3c-restore``).

The so3 **build system can fetch and build that agency itself**, the same way it
fetches AVZ, U-Boot and QEMU — it need not be built out of tree. The ``linux``
Expand Down Expand Up @@ -55,7 +55,7 @@ hypervisor support for it:
* the **vbus / vbstore** clients and the event-channel / grant-table glue
(``soo/kernel/``);
* the hypervisor-side capsule **build / inject / snapshot** code
(``so3/avz/`` — ``capsule_build.c``, ``injector.c``).
(``avz/kernel/`` — ``capsule_build.c``, ``injector.c``).

A capsule-capable guest is produced by ``virt64_capsule_defconfig`` or
``rpi4_64_capsule_defconfig`` (enabling ``CONFIG_SOO``). The agency runs
Expand Down
85 changes: 53 additions & 32 deletions doc/source/coding_conventions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@
"C" coding conventions (Cconv)
##############################

How the style is enforced
*************************

The layout rules below are not merely advisory: they are checked mechanically.
``.clang-format`` at the repository root (itself derived from the Linux kernel
one) is the reference — ``UseTab: Always``, ``IndentWidth: 8``, ``TabWidth: 8``
and ``ColumnLimit: 128`` — and the ``style.yml`` GitHub workflow runs
**clang-format 19** over the tracked sources of ``so3/so3`` and ``so3/usr`` on
every push and pull request.

Run the very same check locally before submitting:

.. code-block:: bash

check-format.sh # list the files that need reformatting
check-format.sh --fix # reformat them in place

Everything clang-format cannot judge — naming, function length, comments, error
handling, what belongs in a macro — is the subject of the rest of this chapter.


Indentation
***********
Expand Down Expand Up @@ -63,7 +83,7 @@ Don't put multiple assignments on a single line either. Coding style
is super simple. Avoid tricky expressions.

Outside of comments, documentation and except in some files where it is required
like Kconfig Linux kernel, spaces are never used for indentation,
like Kconfig Linux kernel, spaces are never used for indentation,
and the above example is deliberately broken.

Get a decent editor and don't leave whitespace at the end of lines.
Expand All @@ -75,11 +95,11 @@ Breaking long lines and strings
Coding style is all about readability and maintainability using commonly
available tools.

The limit on the length of lines should correspond to what a modern screen and
editor is reasonibly able to display before the reader has to scroll horizontally
(even if it is acceptable to scroll over a few characters).
The limit on the length of lines is **128 characters** — what a modern screen and
editor is reasonably able to display without scrolling horizontally. This is the
``ColumnLimit`` of ``.clang-format``, so the check rejects longer lines.

Statements too long will be broken into sensible chunks.
Statements too long will be broken into sensible chunks.
Descendants are always substantially shorter than the parent and
are placed substantially to the right. The same applies to function headers
with a long argument list. However, avoid to break user-visible strings such as
Expand Down Expand Up @@ -183,7 +203,7 @@ Also, prefer using braces when a loop contains more than a single simple stateme
Spaces
======

Use a space after (most) keywords. The notable exceptions are sizeof, typeof, alignof,
Use a space after (most) keywords. The notable exceptions are sizeof, typeof, alignof,
and __attribute__, which look somewhat like functions (and are usually used with parentheses in Linux,
although they are not required in the language, as in: ``sizeof info`` after
``struct fileinfo info;`` is declared).
Expand Down Expand Up @@ -270,8 +290,7 @@ that counts the number of active users, you should call that

Encoding the type of a function into the name (so-called Hungarian
notation) is brain damaged - the compiler knows the types anyway and can
check those, and it only confuses the programmer. No wonder MicroSoft
makes buggy programs.
check those, and it only confuses the programmer.

LOCAL variable names should be short, and to the point. If you have
some random integer loop counter, it should probably be called ``i``.
Expand All @@ -292,9 +311,9 @@ definition of a type or to define a struct and having a more readable
type rather than *struct sensor*. Put a ``_t`` as suffix of a type
definition. For example, ``sensor_t``.

Regarding the platform-dependent definition, it helps to define clear integer types,
Regarding the platform-dependent definition, it helps to define clear integer types,
where the abstraction **helps** avoid confusion whether it is ``int`` or ``long``.
u8/u16/u32 are perfectly fine typedefs
u8/u16/u32 are perfectly fine typedefs

NEVER use a typedef to hide a pointer except for the pointer to a function.
For example:
Expand Down Expand Up @@ -324,17 +343,16 @@ generally easily keep track of about 7 different things, anything more
and it gets confused. You know you're brilliant, but maybe you'd like
to understand what you did 2 weeks from now.

In source files, separate functions with one blank line. If the function is
exported, the **EXPORT** macro for it should follow immediately after the
closing function brace line. E.g.:
In source files, separate functions with one blank line. A function that is not
part of a subsystem's interface should be ``static``; the ones that are belong in
the subsystem header, next to the types they operate on. E.g.:

.. code-block:: c

int system_is_up(void)
static bool system_is_up(void)
{
return system_state == SYSTEM_RUNNING;
}
EXPORT_SYMBOL(system_is_up);

In function prototypes, include parameter names with their data types.
Although this is not required by the C language, it is preferred
Expand Down Expand Up @@ -372,7 +390,7 @@ The rationale for using gotos is:
int result = 0;
char *buffer;

buffer = kmalloc(SIZE, GFP_KERNEL);
buffer = malloc(SIZE);
if (!buffer)
return -ENOMEM;

Expand All @@ -385,7 +403,7 @@ The rationale for using gotos is:
}
...
out_free_buffer:
kfree(buffer);
free(buffer);
return result;
}

Expand All @@ -394,8 +412,8 @@ A common type of bug to be aware of is ``one err bugs`` which look like this:
.. code-block:: c

err:
kfree(foo->bar);
kfree(foo);
free(foo->bar);
free(foo);
return ret;

The bug in this code is that on some exit paths ``foo`` is NULL. Normally the
Expand All @@ -405,9 +423,9 @@ fix for this is to split it up into two error labels ``err_free_bar:`` and
.. code-block:: c

err_free_bar:
kfree(foo->bar);
free(foo->bar);
err_free_foo:
kfree(foo);
free(foo);
return ret;

Ideally you should simulate errors to test all exit paths.
Expand Down Expand Up @@ -541,14 +559,17 @@ Usually, messages do not have to be terminated with a period.
Coming up with good debugging messages can be quite a challenge; and once
you have them, they can be a huge help for remote troubleshooting. However
debug message printing is handled differently than printing other non-debug
messages.
messages.

Syslog-ng
=========
In the kernel, regular messages go through ``printk()`` (``include/printk.h``).
Its low-level counterpart ``lprintk()`` writes straight to the serial port
without going through the console layer, which is what makes it usable very
early at boot, from an interrupt handler, or when the console itself is the
suspect — the assertion and ``BUG_ON()`` paths use it for that reason.

Syslog-ng enables logging messages in various forms and configurations.
It can be used to log message on the console and/or in files typically
stored in ``/var/log`` directory.
Debug traces are kept out of a normal build: define a ``DBG()``-style macro
guarded by a local ``#define DEBUG`` in the subsystem (see
``soo/include/soo/debug.h``) rather than leaving bare ``printk()`` calls behind.

Function return values and names
********************************
Expand Down Expand Up @@ -615,19 +636,19 @@ the next instruction in the assembly output:
Conditional Compilation
***********************

Using #if or #ifdef block should always have a comment on the #else or #endif
Using #if or #ifdef block should always have a comment on the #else or #endif
statement with the name of the condition, like this:

.. code-block:: c

#ifdef CONFIG_SOMETHING

...

#else /* CONFIG_SOMETHING */

...

#endif /* !CONFIG_SOMETHING */

It will greatly help the reading of the code.
Expand Down
26 changes: 23 additions & 3 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# serve to show the default.

import os
import subprocess
import sys
import sphinx
sys.path.insert(0, os.path.abspath('.'))
Expand Down Expand Up @@ -66,11 +67,30 @@
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = u'6.2.0'
# It is derived from the git release tag by the very same helper the kernel boot
# banner uses (so3/so3/scripts/so3version.sh), so the documentation never has to
# be bumped by hand at release time. The helper falls back to the
# SO3_KERNEL_VERSION_FALLBACK constant of so3/so3/include/version.h when the tree
# carries no git metadata — which is the case for the shallow CI checkout that
# publishes these pages.

def _so3_version():
srctree = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'so3', 'so3'))
helper = os.path.join(srctree, 'scripts', 'so3version.sh')

try:
out = subprocess.check_output(['sh', helper, srctree], universal_newlines=True)
except (OSError, subprocess.CalledProcessError):
return 'unknown'

return out.strip() or 'unknown'


# The full version, including alpha/beta/rc tags.
release = u'6.2.0'
release = _so3_version()

# The short X.Y version.
version = '.'.join(release.split('.')[:2])


# The language for content autogenerated by Sphinx. Refer to documentation
Expand Down
2 changes: 1 addition & 1 deletion doc/source/display_input.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ On the QEMU ``virt`` machine SO3 drives a small set of ARM PrimeCell devices for
graphics and human input. These are **not** part of the upstream ``virt`` model;
they are added by the SO3 QEMU patch
(``build/meta-qemu/.../files/0001-qemu-8.2.2-r0/0001-virt.c.patch``) and described
to the kernel in the device tree (``so3/dts/virt64.dts`` / ``virt32.dts``).
to the kernel in the device tree (``so3/so3/dts/virt64.dts`` / ``virt32.dts``).

.. figure:: img/so3_io.png
:width: 100%
Expand Down
5 changes: 3 additions & 2 deletions doc/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ particularly well suited to embedded systems. From a single code base it can be
built in three ways:

* as a **standalone OS** running directly on the hardware (EL1 on ARM64);
* as the **AVZ hypervisor** (*Agency VirtualiZer*) running at EL2, hosting a
single guest at EL1;
* as the **AVZ hypervisor** (*Agency VirtualiZer*) running at EL2, hosting the
agency guest at EL1 — and, with the SOO framework, up to five capsules beside
it;
* as an **SO3 capsule** (S3C) — a lightweight guest on top of AVZ, as part of
the **SOO** framework.

Expand Down
Loading
Loading