This library provides clean, ergonomic, and consistent APIs for low-level and machine-dependent code across a wide variety of low-level environments. It provides some machine-independent APIs abstracting some simple machine dependencies, but mostly provides APIs that are inherently machine-dependent.
lib/arch code is intended to be compatible with a wide variety of low-level environments. Not every API is usable in every context. But everywhere possible, every API is written to be compatible with all these different contexts. For example, due to the Multiboot and EFI environments, nothing uses
long int types and instead everything carefully uses
uintptr_t, etc. to distinguish specific bit widths from pointer and type sizes (pointers are 32 bits in Multiboot,
long int is 32 bits in EFI though pointers are 64 bits).
The main Zircon kernel is about the richest and most forgiving of the environments where
lib/arch code runs. Yet it has more constraints than userspace code.
This is another kernel-like environment that is even more constrained. It's used in some “bare metal”-like contexts that work with the kernel:
These places have many more things they can't do:
constor data is best.
mwait-style features), let alone spawn threads or the like.
Multiboot is a legacy boot method used by some pre-EFI x86 boot loaders such as GRUB, and by QEMU's support for directly booting x86 kernels without a boot loader.
This is a
kernel.phys-like environment, but it's actually x86-32 (i686), i.e. ILP32!
The EFI environment is used to build the Gigaboot boot loader, or other tests and tools that run in EFI. This uses the Windows object file format (PE-COFF), and so code is compiled by what‘s essentially a Windows toolchain (e.g.
long int is 32 bits while pointers are 64 bits). It’s the same Clang C++ front end, but the calling convention, object file, and linking details are like Windows.
lib/arch is primarily geared towards the needs of privileged (kernel) code. But applicable pieces can also be used in userspace, e.g.
lib/arch code is largely standalone leaf functions without its own dependencies. But some other header and library dependencies are both acceptable and encouraged in
Users are not permitted to directly depend on libraries strictly under
lib/arch, as they are implementation details; only
lib/arch itself may be depended on.
lib/arch should be entirely compatible with a full-fledged standard C library. But it should depend only on the most minimal “bare metal” subset of library APIs. In privileged environments,
lib/arch uses the Zircon kernel libc. This provides only these standard APIs:
stderrbut only those two (which might actually be the same one) exist and no others can be created
stderris probably not thread-safe or interrupt-safe, may busy-wait slowly for large output strings, etc.
static_assert whenever possible. All runtime assertions should use
<zircon/assert.h>. This is available (with various different implementations of
__zx_panic) in all the supported environments.
ktl subset of standard C++ library functionality can be used freely in
lib/arch and phys code. For an API that makes sense in userspace, the standard headers and
std:: names can be used directly instead of the
ktl:: wrappers--but care must be taken to stick to the subset that are exported with
ktl:: wrappers, as only those are approved for use in kernel code.
Other libraries can be used as long as they are compatible with the kernel's constraints. This is a non-exhaustive list:
lib/arch interfaces should be well-isolated, well-documented, clean APIs.
Hardware bit layouts are expressed using
hwreg types, never with ad hoc
#define or direct use of constants.
All header files are under
<lib/arch/...>. Some headers provide machine-independent APIs and some provide machine-specific APIs and some provide a mix of the two. In general a common header file name is used for the same API topic across machines even if the actual APIs are partly or wholly machine-specific. This reduces the number of places where the code using the library needs
#if tests for each machine. For example,
<lib/arch/intrin.h> provides many machine-specific APIs as well as a few machine-independent APIs but there aren't separate
<lib/arch/x86/intrin.h> names to
There is a source subdirectory for each machine, named by the kernel‘s name for the CPU (
$zircon_cpu in GN, i.e.
arm64 or not
x86). Each is actually a
library() target of its own, but users of
lib/arch don’t know the sub-library exists, they just use
deps = [ "//zircon/kernel/lib/arch" ]. The machine-specific subdirectory provides
include/lib/arch/... files for the machine-dependent header files with machine-independent file names.
These subdirectories should be reserved for the things that really can't be compiled on a different machine, such as code using inline
asm or intrinsics. Declarations that are simply about only one machine should go into the main
include/lib/arch/... directory, possibly under a machine-specific subdirectory there. Things like
hwreg declarations for system registers are topically particular to one machine architecture, but the declarations themselves can and should be portable to any machine and indeed to any operating system. That makes it possible to use these headers in
hwreg::AsmHeader generator programs, which are compiled on the build host. Such declarations may also be useful in unit test code that can sometimes be built and tested on a different machine and/or operating system.
There is also a
host subdirectory akin to the machine subdirectories. This is used in lieu of a particular machine when compiling for host environments. This makes it possible to write code using the machine-independent lib/arch API that can be built on host for purposes lock mock testing.
Only C++ 17 with modern style is supported. There is no provision for C or for C++ 14.
lib/arch C++ APIs use the
All public APIs are documented with clang-doc-style
/// comments before the declaration.
Assembly code is minimized, preferring to use compiler intrinsics or inline
__asm__ in C++ code whenever that's possible. Standalone assembly code is in
.S files with straightforward style using two-space indentation and C++-style
// comments, and uses
<lib/arch/asm.h> macros for symbol definitions.
Header files that are compatible with assembly use
#ifdef __ASSEMBLER__ to separate assembly-only and C++-only declarations. All header files are compatible with C++ even if they have nothing outside
Macros for assembly code have an assembly API flavor and are defined as GAS assembly macros using
.macro, not as C macros using
#define. Assembly macro APIs are documented using
/// comments before the
Public macros that do not generate instructions have names starting with
., such as
<lib/arch/asm.h>. Macros that generate instructions have instruction-like names with no particular prefix.
Internal macros not used outside a header file have names starting with
_ (and thus
_. for non-instruction-generating macros) and do not get
Isolated trivial integer constants used in both C++ and assembly can be defined in header files using
#define. However, most constants should be defined in C++ using
constexpr (often via
hwreg types). When assembly code needs to use those values, create a generated header file using the
hwreg::AsmHeader API and the
hwreg_asm_header() GN template.
The shorthands of "[<archicture ID>/<manual ID>]: <chapter/verse/page/figure/etc.>" are used extensively to reference official architecture documentation. In particular, the identifiers are expected to be one of the following:
[arm/v8] ~ Arm Architecture Reference Manual Armv8, for Armv8-A architecture profile, July 17 2020
[arm/sysreg] ~ Arm® Architecture Registers Armv8, for Armv8-A architecture profile, September 29 2020
[arm/psci] ~ Arm Power State Coordination Interface, Platform Design Document, June 2021
[intel/vol1] ~ Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 1: Basic Architecture, May 2020
[intel/vol2] ~ Intel® 64 and IA-32 Architectures Software Developer's Manual Combined Volumes 2A, 2B, 2C, and 2D: Instruction Set Reference, A-Z, May 2020
[intel/vol3] ~ Intel® 64 and IA-32 Architectures Software Developer's Manual Combined Volumes 3A, 3B, 3C, and 3D: System Programming Guide, May 2020
[intel/vol4] ~ Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 4: Model-Specific Registers, May 2020
[intel/drng] ~ Intel® Digital Random Number Generator (DRNG) Software Implementation Guide, Oct 2018
[amd/vol1] ~ AMD64 Architecture Programmer’s Manual Volume 1: Application Programming, December 2017
[amd/vol2] ~ AMD64 Architecture Programmer’s Manual Volume 2: System Programming, May 2020
[amd/vol3] ~ AMD64 Architecture Programmer’s Manual Volume 3: General Purpose and System Instructions, April 2020
[amd/vol4] ~ AMD64 Architecture Programmer’s Manual Volume 4: 128-bit and 256 bit media instructions, May 2020
[amd/vol5] ~ AMD64 Architecture Programmer’s Manual Volume 5: 64-Bit Media and x87 Floating-Point Instructions, May 2018
[amd/sog/17h] ~ Software Optimization Guide for AMD Family 17h Processors, June 2017
[amd/rg/17h/00h-0Fh] ~ Revision Guide for AMD Family 17h Models 00h-0Fh Processors, June 2018
[amd/ppr/17h/01h,08h] ~ Processor Programming Reference (PPR) for AMD Family 17h Models 01h,08h, Revision B2 Processors, June 2019
[amd/ibc] ~ AMD64 Technology: Indirect Branch Control Extension
[amd/ssbd] ~ AMD64 Technology: Speculative Store Bypass Disable
lib/arch/testing provides a library of testing utilities subject to all of the above constraints. When tests that run in kernel or
phys environments are not necessary, it is usually much easier to test related code using userland
gtest tests that don't need to meet all the constraints of the code under test itself.
TODO(mcgrathr) Describe testing methodology.