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: which [Driver] classes the build registers, in what order, how a kernel project keeps one out or opts one in, and what the generator reports when it cannot do what a class asks. For where the manifest runs in the boot sequence, see Startup.
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:
// <auto-generated/> CosmosEntryPoint.g.cs
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
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());
}
}
}
One Register call per driver, each constructing the driver with its parameterless constructor. The manifest is generated even when no driver survives; its Register body is then empty. The registry it calls is on the kit's experimental seam, and the drivers Cosmos.Kernel 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:
| 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: it cannot carry the attribute.
The drivers Cosmos ships take the second path. Cosmos.Kernel.Drivers holds PciHostDriver, PcieRootPortDriver and VirtioPciTransportDriver (under the Pci feature), E1000EDriver and VirtioNetDriver (under Network), VirtioGpuDriver and VmwareSvgaDriver (under Graphics), AhciDriver, NvmeDriver and VirtioBlkDriver (under Storage), XhciDriver, UsbHubDriver, UsbKeyboardDriver and UsbMassStorageDriver (under Usb), Ps2KeyboardDriver (under Keyboard), Ps2MouseDriver (under Mouse), and VirtioMmioTransportDriver, VirtioInputDriver and I8042Driver (no feature), and the Cosmos.Kernel aggregator every kernel references carries the package, so all nineteen are in every kernel's manifest after its own drivers, ordered by their full type names, without the kernel referencing the package itself. 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.
To be registered a class must be concrete (not abstract, not static), 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.
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 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); 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. A host test in tests/Cosmos.Tests.SourceGenerators keeps the two lists in step.
Policy: excluding and opting in
A kernel project shapes its manifest 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. 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: a driver the kernel excludes or does not opt into is never reported, so the diagnostics below describe what the manifest would register.
Diagnostics
| 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
The generators read three build properties, made visible to them by CompilerVisibleProperty items in Cosmos.Kernel.SourceGenerators.props (shipped in the generator package); the entry point generator reads CosmosKernelClass alone, 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 are worth knowing when touching it. The items are joined inside a target rather than in an evaluation-time PropertyGroup, because an item reference does not expand there; the target runs BeforeTargets="GenerateMSBuildEditorConfigFileCore", which is the step that writes the compiler's editorconfig. And the separator is a comma: 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")], so a kernel or a library that declares a [Driver] class 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 that 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; 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:
dotnet test tests/Cosmos.Tests.SourceGenerators -c Debug
CI runs it as the source-generator-tests job of .github/workflows/dotnet.yml.