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 uint64_t
, 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.
memcpy
.kernel.phys
environmentThis 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:
constinit
rules.data.rel.ro
&something
or function_ptr
"string literal"
for const char*
const
or 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/asm.h>
.
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 lib/arch
.
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:
<ctype.h>
functions<string.h>
functionsabort
, strtol
, strtotul
from <stdlib.h>
printf
family from <stdio.h>
snprintf
and vsnprintf
printf
and vprintf
, fprintf
and vfprintf
FILE*
, stdout
, and stderr
but only those two (which might actually be the same one) exist and no others can be createdprintf
/ fprintf
output to stdout
/stderr
is probably not thread-safe or interrupt-safe, may busy-wait slowly for large output strings, etc.Prefer 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
and std
The 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/arm64/intrin.h>
and <lib/arch/x86/intrin.h>
names to #include
.
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.
All lib/arch
C++ APIs use the arch
namespace.
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 #ifdef __ASSEMBLER__
.
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 .macro
declaration.
Public macros that do not generate instructions have names starting with .
, such as .function
and .object
in <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 ///
comments.
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/smccc] ~ SMC Calling Convention, May 2022
[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 zxtest
or gtest
tests that don't need to meet all the constraints of the code under test itself.
TODO(mcgrathr) Describe testing methodology.