Driver manifest
Every Cosmos kernel gets two generated files from Cosmos.Kernel.SourceGenerators: the entry point, and the driver manifest. This page describes the manifest: what the two generators write, which [Driver] classes the build registers and in what order, how a feature switch guards a registration, how a kernel project keeps a driver out or opts one in, and what the generator reports when it cannot do what a class asks; the build plumbing, the visibility rules and the tests behind it close the page. For where the manifest runs in the boot sequence, see Startup.
The manifest exists because nothing else can find a driver. A kernel is compiled ahead of time and kernel code does not use reflection, so the kernel cannot search its assemblies for driver classes when it boots, and trimming leaves any class that no code references out of the image. The manifest is the code that references each driver: a source generator writes it at build time from the [Driver] classes it finds and the project's policy, and a driver it does not construct is not in the kernel. Its order matters too, as the last key when the kit chooses between drivers for a device. These concepts, and the hardware ones a driver builds on, have background notes in the driver glossary, and Writing a driver covers the kit from the driver author's side.
What is generated
Each file has its own generator. CosmosEntryPointGenerator writes the entry point from the CosmosKernelClass property alone, so editing the kernel's sources or its driver policy leaves it cached; DriverManifestGenerator writes the manifest from the drivers it finds and the policy. When the property is empty, neither file is generated. When it is set, the entry point always calls the manifest first. The call is explicit, not a module initializer, so the drivers are registered in manifest order, right before the kernel starts. Both files are shown here without their doc comments and [GeneratedCode] attributes:
// <auto-generated/> CosmosEntryPoint.g.cs
namespace Cosmos.Kernel.System.Internal;
public static class CosmosEntryPoint
{
public static void Main()
{
global::Cosmos.Kernel.System.Internal.DriverManifest.Register();
global::Cosmos.Kernel.System.Internal.KernelEntry.Start(new global::MyOS.Kernel());
}
}
// <auto-generated/> DriverManifest.g.cs
// The driver registry is on the experimental driver kit seam (COSMOS0003).
// This file is generated, so it acknowledges the seam for itself alone;
// a driver this kernel declares still meets the diagnostic in its own code.
#pragma warning disable COSMOS0003
namespace Cosmos.Kernel.System.Internal;
internal static class DriverManifest
{
public static void Register()
{
global::Cosmos.Kernel.HAL.DriverKit.DriverRegistry.Register(new global::MyOS.Drivers.BoardDriver());
if (global::Cosmos.Kernel.System.KernelFeatures.Network)
{
global::Cosmos.Kernel.HAL.DriverKit.DriverRegistry.Register(new global::Acme.Drivers.AcmeNicDriver());
}
}
}
The manifest holds one Register call per driver, each constructing the driver with its parameterless constructor. It is generated even when no driver survives, with an empty Register body. The registry it calls is on the kit's experimental seam, and the drivers Cosmos ships put a call in every kernel's manifest, so the file opens by disabling the seam's diagnostic id for itself: the kernel author did not write it, and a kernel that has not suppressed the id (see Visibility) still compiles.
With EmitCompilerGeneratedFiles on, which Cosmos.Sdk sets by default, both files are written under the intermediate directory, one folder per generator:
$(IntermediateOutputPath)/generated/Cosmos.Kernel.SourceGenerators/
Cosmos.Kernel.SourceGenerators.CosmosEntryPointGenerator/CosmosEntryPoint.g.cs
Cosmos.Kernel.SourceGenerators.DriverManifestGenerator/DriverManifest.g.cs
Reading DriverManifest.g.cs there is the quickest way to check which drivers a kernel carries.
How drivers are found
A driver is a class marked [Cosmos.Kernel.HAL.DriverKit.Driver]. The generator looks in two places and keeps their order fixed, because manifest position is the last arbitration key when two drivers of equal priority and specificity match the same device (driver matching):
| Source | Which classes | Order |
|---|---|---|
| The kernel project itself | Every marked class the assembly can construct from its top level (a private nested class is reported with COSMOSGEN001 and left out) |
By file path (ordinal), then by position in the file |
Referenced assemblies that reference Cosmos.Kernel.HAL (or are it) |
Marked classes the kernel assembly can see: public ones, and internal ones under an InternalsVisibleTo grant |
By assembly name, then by full type name, both ordinal |
The kernel's own drivers come first, then the referenced ones. An assembly that does not reference Cosmos.Kernel.HAL is not searched, since it cannot carry the attribute.
To be registered, a class must be concrete (not abstract, not static) and non-generic, derive from Cosmos.Kernel.HAL.DriverKit.Driver, and have a parameterless constructor the kernel assembly can call. A marked class that fails one of these is reported (COSMOSGEN001) and left out.
The drivers Cosmos ships take the second path. They live in Cosmos.Kernel.Drivers, which the Cosmos.Kernel aggregator every kernel references carries, so all twenty-five are in every kernel's manifest after its own drivers without the kernel referencing the package itself. They are ordered by full type name, and a shipped driver's full name carries its bus kind, category and driver folder (Cosmos.Kernel.Drivers.Pci.Storage.Nvme.NvmeDriver), so that order groups them by bus kind (Pci, Platform, Ps2, Usb, Virtio) and, within one, by category. Twenty-two of them are registered under a feature guard, and three under none:
| Feature | Drivers |
|---|---|
Pci |
PciHostDriver, PcieRootPortDriver, VirtioPciTransportDriver |
Network |
E1000EDriver, VirtioNetDriver |
Graphics |
VirtioGpuDriver, VmwareSvgaDriver, AmdDcnDriver, IntelGraphicsDriver |
Storage |
AhciDriver, NvmeDriver, VirtioBlkDriver |
Audio |
HdAudioDriver |
Usb |
XhciDriver, EhciDriver, OhciDriver, UsbHubDriver, UsbKeyboardDriver, UsbMouseDriver, UsbMassStorageDriver |
Keyboard |
Ps2KeyboardDriver |
Mouse |
Ps2MouseDriver |
| None | VirtioMmioTransportDriver, VirtioInputDriver, I8042Driver |
Feature guards
[Driver(Feature = DriverFeature.Network)] ties the registration to a feature switch. The generator wraps the Register call in an if on the Cosmos.Kernel.System.KernelFeatures property of the same name, one property per if and nothing else in the condition, which is the shape ILC folds to a constant. A kernel that sets CosmosEnableNetwork to false then never constructs the driver, and since nothing else constructs a shipped driver, everything only that driver references is trimmed with it.
The DriverFeature enum members mirror the KernelFeatures properties by name (Interrupts, Uart, Pci, Timer, Keyboard, Mouse, Network, Storage, Fat, Graphics, Scheduler, Usb, Audio), and None emits no guard. The generator resolves the property in the kernel's compilation, so a member without a property is a build error (COSMOSGEN003) rather than a manifest that does not compile. FeatureParityTests in tests/Cosmos.Tests.SourceGenerators keeps the two lists in step.
Excluding and opting in
A kernel project shapes its manifest, its driver policy, with two item types:
<ItemGroup>
<!-- Drop a driver the build would otherwise register. -->
<CosmosDriverExclude Include="Acme.Drivers.AcmeNicDriver" />
<!-- Register a driver declared [Driver(Default = false)]. -->
<CosmosDriverInclude Include="Acme.Drivers.ExperimentalGpu" />
</ItemGroup>
Names are full type names in C# form, without global::, with nested types joined by dots (MyOS.Drivers.Bus.Child). A shipped driver is excluded the same way, by its full type name: <CosmosDriverExclude Include="Cosmos.Kernel.Drivers.Pci.Network.E1000E.E1000EDriver" /> keeps the Intel driver out of the manifest.
An excluded driver is dropped whatever its Default, and a driver with Default = false is registered only when a CosmosDriverInclude item names it. An item that matches no [Driver] class the kernel can see is reported (COSMOSGEN002), which is how a stale or misspelled entry shows up. Policy is applied before validation, so a driver the kernel excludes or does not opt into is never reported, and the diagnostics below describe what the manifest would register.
Diagnostics
The generator reports three diagnostics, all in the Cosmos.DriverKit category:
| Id | Severity | Meaning |
|---|---|---|
COSMOSGEN001 |
Warning | A [Driver] class is left out: it is abstract, static, generic, does not derive from Driver, has no parameterless constructor the kernel assembly can call, or is not accessible from the kernel assembly. The message names the reason. |
COSMOSGEN002 |
Warning | A CosmosDriverExclude or CosmosDriverInclude item matches no [Driver] class visible to the kernel. |
COSMOSGEN003 |
Error | A [Driver] names a DriverFeature member with no KernelFeatures property of that name, so the registration cannot be guarded. |
The rules are listed in src/Cosmos.Kernel.SourceGenerators/AnalyzerReleases.Unshipped.md, the release tracking format the Roslyn analyzers package expects.
Build plumbing
A generator reads only the build properties the project makes visible to it (source generators). These generators read three, made visible by CompilerVisibleProperty items in Cosmos.Kernel.SourceGenerators.props, which ships in the generator package. The entry point generator reads CosmosKernelClass alone, and the manifest generator all three:
| Property | Set by | Read as |
|---|---|---|
CosmosKernelClass |
The kernel project, or Sdk.props from RootNamespace |
build_property.CosmosKernelClass |
CosmosDriverExcludeList |
The CosmosCollectDriverPolicy target in Sdk.targets, joining the CosmosDriverExclude items |
build_property.CosmosDriverExcludeList |
CosmosDriverIncludeList |
The same target, joining the CosmosDriverInclude items |
build_property.CosmosDriverIncludeList |
Two details of that target matter to anyone who changes it. The items are joined inside a target rather than in an evaluation-time PropertyGroup, because an item reference does not expand there, and the target runs BeforeTargets="GenerateMSBuildEditorConfigFileCore", the step that writes the compiler's editorconfig. The separator is a comma, because the compiler reads that file as an editorconfig, where a semicolon starts a comment, so a semicolon-joined list would be cut after its first name.
Visibility
The kit is public, under the experimental seam COSMOS0003: Driver, DriverAttribute, DriverRegistry and every other type under Cosmos.Kernel.HAL.DriverKit carry [Experimental("COSMOS0003")]. A kernel or a library that declares a [Driver] class therefore suppresses that id in its .csproj, and a driver library also declares itself a driver assembly:
<PropertyGroup>
<NoWarn>$(NoWarn);COSMOS0003</NoWarn>
<!-- A class library of drivers only: held to the public surface by the analyzer. -->
<CosmosDriverAssembly>true</CosmosDriverAssembly>
</PropertyGroup>
A kernel that declares no driver of its own needs no suppression. The generated manifest disables the id for itself, since its author did not write it, and it is the only place such a kernel names the kit; the suppression is for the driver code a kernel or a library declares. Writing a driver covers the kit and the project lines from the driver author's side, and Public API Tracking covers what a driver assembly is held to.
Testing
tests/Cosmos.Tests.SourceGenerators runs both generators on the host against in-memory compilations, with stubs standing in for the kit and the ring, and checks the generated text and the diagnostics exactly. CachingTests checks that adding a driver regenerates the manifest and leaves the entry point cached. The suite runs on its own with:
dotnet test tests/Cosmos.Tests.SourceGenerators -c Debug
CI runs it as the source-generator-tests job of .github/workflows/dotnet.yml.