This doc is a short guide to adding a new Zircon syscall. The intended audience is kernel developers or anyone who is adding a new syscall.
Keep in mind that this guide only covers the mechanics of adding syscall/objects. It's not a style guide, rubric, and does not provide any advice on how to know when an API is stable enough or mature enough to become part of the system interface.
Because our system continues to change, you should expect that this guide is out of date. Sorry. If you are following this guide and you notice errors or omissions, please make an attempt to update it. Thank you!
There are a number of ways you can structure the changes to add a new Zircon syscall (or object). This guide will describe an approach consisting of four CLs: stub, implementation, extras, and annotations.
We‘re going to follow the “rewrite history” method. Zircon syscalls do not (currently) follow Platform Versioning so instead we’re going to make it look as if the new syscall had been there all along. To do this, we'll need to update some SDK history files for frozen API levels.
In this CL, you‘ll define the syscall and provide a stub implementation that simply returns ZX_ERR_NOT_SUPPORTED
. Be sure to include a core-test in this stub CL that verifies the caller doesn’t crash and that the new call returns the error as expected.
The build system and FIDL compiler will automatically generate some of the boilerplate for you. To take advantage, you'll want to get in the habit of performing a default build, fx build
, and running fx check-goldens
after you make changes to FIDL files.
To illustrate, let's look at a stub CL that adds both a new Zircon object (counter
) and a new syscall (zx_counter_create
).
The stub CL has four parts.
Next, define the new syscall in an existing .fidl
file or add a new file if appropriate. Be sure to add a commented out @available(added=HEAD)
annotation that indicates the new syscall (or object if adding a new Zircon object) is added at HEAD.
In the rarer event of adding a new Zircon object, you'll need to take a few extra steps.
a. Teach the FIDL compiler about the new object type. The FIDL toolchain bakes in knowledge of object types. This isn‘t elegant, but it avoids some circular dependencies. You’ll have to update parts of the FIDL toolchain to understand your new object type. In particular, update:
HandleSubtype
in //tools/fidl/fidlc/src/properties.h
NameHandleSubtype
in //tools/fidl/fidlc/src/names.cc
HandleType
in //tools/fidl/abi-compat/src/compare/handle.rs
handle-subtype
in //tools/fidl/fidlc/schema.json
GoodHandleSubtype
in //tools/fidl/fidlc/tests/types_tests.cc
HandleSubtype
and ObjectType
consts in //tools/fidl/lib/fidlgen/types.go
handleSubtypes
, handleSubtypesFdomain
and objectTypeConsts
in //tools/fidl/fidlgen_rust/codegen/ir.go
b. Update the fidlc
goldens by following the instructions provided in the build error you get when you build without updating them.
c. Add the new object type to zircon/types.h.
d. Update the //sdk/history/*/zx.api_summary.json
files. These are for the public zx
FIDL library, which shares zx_common.fidl
with the syscall library.
e. Extend docsgen as necessary.
Implement the syscall somewhere in zircon/kernel/lib/syscalls/, with a prefix of sys_
instead of zx_
. For now, the implementation should simply return ZX_ERR_NOT_SUPPORTED
.
Note: The sys_
function‘s signature isn’t necessarily the same as the zx_
function‘s signature. If you’re unsure of the right sys_
signature you run a build and check the resulting $root_build_dir/fidling/gen/zircon/vdso/zx/zither/kernel/lib/syscalls/kernel.inc
file. Or check the build output for a linker error that tells you the missing sys_
function's expected signature.
Add a new or extend an existing lib/zx wrapper in order to provide a C++ API for the new syscall. Be sure to apply a ZX_AVAILABLE_SINCE(HEAD)
annotation to your new C++ method (or class if it's a new Zircon object). While the syscall itself will appear to have been present from the very beginning, the C++ wrapper is added at HEAD.
Add a core-test that verifies the stub returns the right error value (and does not crash the system). By using the new lib/zx
wrapper in this test, you can be sure it also works as expected.
Once you've gotten the stub to build and pass CQ, land it. Next, move on to the implementation CL.
To enable the use of a new Zircon object (i.e. zx.Handle:COUNTER
) in the C++ and Rust language bindings, there are a few additional areas that must be updated. These updates allow the language binding code generators to know how to handle a new Zircon object.
For FIDL C++ language bindings, update:
ShortObjTypeName
in //src/lib/fidl_codec/display_handle.cc
DisplayObjType
in //src/lib/fidl_codec/printer.cc
//sdk/lib/fidl/hlcpp/include/lib/fidl/cpp/internal/header.h
handleSubtypeConsts
in //tools/fidl/lib/fidlgen_cpp/handles.go
zxNames
in //tools/fidl/lib/fidlgen_cpp/name_transforms.go
For FIDL Rust language bindings, update:
ObjectType
in //sdk/rust/zx/src/handle.rs
invoke_for_handle_types
in //src/lib/fuchsia-async/src/handle/mod.rs
Now it's time to replace the stub implementation with a real one, add real tests and real documentation. Built it, pass CQ, land it, and move on.
Once you‘ve got a working implementation with C/C++ bindings, you’re now ready to add Rust bindings, and update (fidl_codec
(used by fidlcat
):
ShortObjTypeName
in display_handle.cc
PrettyPrinter::DisplayObjType
in printer.cc
The above list of “extras” may be incomplete so this is a good time look around ad other syscalls to see if there are more things you need to update (tools? bindings for other languages?). Land the extras CLs and move on to the final step.
Now that the implementation and extras have landed, go back and change the value you added in the @available
and ZX_AVAILABLE_SINCE
annotation(s) from HEAD
to NEXT
to make the syscall available in the next stable API level.