> For the complete documentation index, see [llms.txt](https://docs.enclaive.cloud/sylica/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.enclaive.cloud/sylica/tutorials/building-sylica-firmware.md).

# Building Sylica Firmware

This document describes the Sylica build process.

### Build Environments

Sylica provides two build workflows:

* **Local development build** — fast builds using the host toolchain, suitable for firmware development and debugging.
* **Isolated build** — builds inside the pinned Sylica container environment, used for CI, releases, and reproducibility.

Both workflows use the same underlying firmware build implementation in `reproduce/build.sh`. They differ only in how the build environment and dependencies are prepared.

| Use case                   | Build method                                   |
| -------------------------- | ---------------------------------------------- |
| Firmware development       | `scripts/build.sh <platform>`                  |
| CI                         | Isolated Docker build                          |
| Release build              | Isolated Docker build                          |
| Reproduce a release        | Isolated Docker build                          |
| Test build reproducibility | `scripts/verify-reproducibility.sh <platform>` |

Release and reproducibility verification MUST use the isolated build. See [Reproducible Builds](https://chatgpt.com/c/reproducibility.md) for the deterministic build inputs and independent release-verification procedure.

### Build architecture

Sylica uses one common firmware build implementation for local, CI, and release builds.

Local development uses:

```
scripts/build.sh
       │
       │ initialize dependencies
       ▼
reproduce/build.sh
       │
       │ configure and build EDK2
       ▼
    CVMF.fd
```

CI and release builds use:

```
reproduce/Dockerfile
       │
       │ pinned build environment
       │ network disabled during compilation
       ▼
reproduce/build.sh
       │
       │ configure and build EDK2
       ▼
    CVMF.fd
```

The scripts have distinct responsibilities:

| Component                           | Responsibility                                                 |
| ----------------------------------- | -------------------------------------------------------------- |
| `scripts/build.sh`                  | User-facing entry point for local development builds           |
| `reproduce/build.sh`                | Common EDK2 firmware build implementation                      |
| `reproduce/Dockerfile`              | Pinned and isolated build environment                          |
| `scripts/verify-reproducibility.sh` | Performs independent builds and compares their firmware hashes |

Normal development should use `scripts/build.sh`. Direct invocation of `reproduce/build.sh` is primarily intended for the build infrastructure.

### Platforms

Every build targets a platform defined in:

```
platforms/<platform>.json
```

The available platforms include:

| Platform         | Purpose                                       | Output    |
| ---------------- | --------------------------------------------- | --------- |
| `sylica-sev-oss` | Sylica firmware for AMD SEV-SNP               | `CVMF.fd` |
| `sylica-tdx-oss` | Sylica firmware for Intel TDX                 | `CVMF.fd` |
| `sylica-x86`     | Sylica firmware for non-confidential x86 VMs  | `CVMF.fd` |
| `debug-sev`      | Unmodified upstream AMD SEV reference build   | `OVMF.fd` |
| `debug-tdx`      | Unmodified upstream Intel TDX reference build | `OVMF.fd` |

See [Platforms](/sylica/documentation/supported-platforms.md) for the platform-specific firmware configuration and purpose of the reference builds.

### Local development build

Use the local build when developing or debugging Sylica firmware.

For AMD SEV-SNP:

```bash
scripts/build.sh sylica-sev
```

For Intel TDX:

```bash
scripts/build.sh sylica-tdx
```

For non-confidential x86 VMs:

```bash
scripts/build.sh sylica-x86
```

The local build runs directly on the host and can reuse EDK2 build state between invocations, making it suitable for iterative development.

#### Prerequisites

The local host must provide the firmware build toolchain. `scripts/build.sh` verifies the presence of:

```
gcc
make
nasm
iasl
python3
```

The canonical package and toolchain definition is maintained in `reproduce/Dockerfile`.

The development wrapper does not install missing dependencies. If a required command is unavailable, the build exits with an error such as:

```
missing tool: nasm (see reproduce/Dockerfile)
```

Git is also required because the wrapper initializes the repository's submodules before starting the firmware build.

#### What `scripts/build.sh` does

The development wrapper performs the repository-level preparation required for a build:

1. Validates that `platforms/<platform>.json` exists.
2. Checks that the required build tools are available.
3. Initializes the Sylica Git submodules.
4. Initializes the nested edk2 Git submodules.
5. Executes `reproduce/build.sh <platform>`.

Conceptually:

```
scripts/build.sh sylica-sev
          │
          ├── validate platform
          ├── check host tools
          ├── initialize Sylica submodules
          ├── initialize edk2 submodules
          │
          ▼
reproduce/build.sh sylica-sev
          │
          ▼
       CVMF.fd
```

Network access may be required while `scripts/build.sh` initializes missing Git submodules. The actual firmware build operates on the source and dependencies already present in the working tree.

#### Development build limitations

A local build uses the compiler, libraries, and build tools installed on the developer's host.

Different package or compiler versions can therefore produce output that differs from an official Sylica release.

Local builds are suitable for:

* development;
* debugging;
* testing firmware changes;
* incremental EDK2 builds.

They are not the reference environment for release generation or reproducibility verification.

Use the isolated build for those purposes.

### Isolated build

CI, releases, and reproducibility use the build environment defined in:

```
reproduce/Dockerfile
```

Before building, initialize the required Git submodules:

```bash
git submodule update --init --depth 1
git -C edk2 submodule update --init --depth 1
```

Then build the required platform.

For AMD SEV-SNP:

```bash
docker build \
    -f reproduce/Dockerfile \
    --target artifact \
    --build-arg PLATFORM=sylica-sev \
    -o out \
    .
```

For Intel TDX:

```bash
docker build \
    -f reproduce/Dockerfile \
    --target artifact \
    --build-arg PLATFORM=sylica-tdx \
    -o out \
    .
```

The repository is copied into the container before compilation. The actual firmware build then runs with network access disabled:

```dockerfile
RUN --network=none reproduce/build.sh "$PLATFORM"
```

This ensures that compilation uses only the source and dependencies already present in the build environment.

#### Container stages

The build container contains three stages:

| Stage      | Purpose                                                        |
| ---------- | -------------------------------------------------------------- |
| `env`      | Creates the pinned firmware toolchain environment              |
| `build`    | Copies the repository and builds the selected platform offline |
| `artifact` | Exports the resulting build artifacts                          |

The `env` stage uses an Ubuntu base image pinned by digest and installs the toolchain from a fixed Ubuntu package snapshot.

It also records the exact installed package versions in:

```
/tools-manifest.txt
```

The `build` stage executes the common `reproduce/build.sh` implementation.

The final `artifact` stage contains only the generated files from `out/`, allowing Docker BuildKit to export them directly to the host.

See [Reproducible Builds](/sylica/tutorials/reproducing-builds.md) for details about the pinned inputs and deterministic build environment.

### Build output

Artifacts are written to:

```
out/<platform>/
```

For an isolated `sylica-sev` build:

```
out/sylica-sev/
├── CVMF.fd
├── sha256sums
├── b2sums
└── tools-manifest.txt
```

`CVMF.fd` is the Sylica firmware image.

`sha256sums` contains the SHA-256 digest of the firmware:

```bash
cat out/sylica-sev/sha256sums
```

`b2sums` contains its BLAKE2 digest:

```bash
cat out/sylica-sev/b2sums
```

`tools-manifest.txt` records the exact packages installed in the isolated build environment.

A local development build does not produce `tools-manifest.txt`, because it uses the host toolchain rather than the pinned container toolchain.

### How the firmware build works

The actual firmware compilation is implemented by:

```
reproduce/build.sh
```

It is shared by both local and isolated builds.

The script performs the following high-level operations:

1. Loads and validates the selected platform configuration.
2. Verifies that required edk2 submodules are initialized.
3. Prepares the deterministic build environment.
4. Initializes the EDK2 build configuration.
5. Builds EDK2 BaseTools.
6. Prepares Sylica-specific platform packages.
7. Invokes the EDK2 build system.
8. Copies the firmware into `out/<platform>/`.
9. Generates SHA-256 and BLAKE2 checksums.

The resulting EDK2 invocation is derived from the selected platform configuration:

```bash
build \
    -p "$(sylica platform)" \
    -b "$(sylica target)" \
    -a "$(sylica arch)" \
    -t "$(sylica toolchain)" \
    $(sylica arguments)
```

This keeps platform-specific settings separate from the common build implementation.

Deterministic inputs such as timestamps, Python hashing, stack-cookie values, the toolchain, and the container environment are described in [Reproducible Builds](https://chatgpt.com/c/reproducibility.md).

### Platform configuration

A platform build recipe is defined by:

```
platforms/<name>.json
```

The configuration determines how `reproduce/build.sh` invokes EDK2.

| Key         | Meaning                                             |
| ----------- | --------------------------------------------------- |
| `package`   | Sylica platform package or upstream reference build |
| `platform`  | EDK2 DSC file to build                              |
| `target`    | EDK2 build target, such as `RELEASE` or `DEBUG`     |
| `arch`      | Target architecture                                 |
| `toolchain` | EDK2 toolchain                                      |
| `arguments` | Additional EDK2 build arguments                     |
| `output`    | Path of the generated firmware image                |
| `filename`  | Name of the exported firmware image                 |
| `measure`   | Measurement mode used after the build               |

For example, selecting:

```bash
scripts/build.sh sylica-sev
```

causes the build infrastructure to load:

```
platforms/sylica-sev.json
```

and use that configuration to determine the DSC and EDK2 build parameters.

### Adding a platform

To add another firmware platform:

1. Add or reference the required DSC and FDF configuration.
2. Create `platforms/<name>.json`.
3. Verify the platform locally with:

   ```bash
   scripts/build.sh <name>
   ```
4. Verify deterministic builds with:

   ```bash
   scripts/verify-reproducibility.sh <name>
   ```
5. Add the platform to the CI matrix in `.github/workflows/build.yml`.
6. Configure the appropriate platform measurement mode.

Measurement handling is technology-specific. AMD SEV-SNP and Intel TDX use different launch-measurement mechanisms even though they share the same Sylica build infrastructure.

See [Platforms](/sylica/documentation/supported-platforms.md) for platform-specific details.

### Reproducibility verification

To verify that a platform builds deterministically:

```bash
scripts/verify-reproducibility.sh <platform>
```

For example:

```bash
scripts/verify-reproducibility.sh sylica-sev
```

The script performs isolated builds and compares the resulting firmware SHA-256 digests.

A successful test reports:

```
REPRODUCIBLE
```

This verifies build determinism. Reproducing a published Sylica release additionally requires comparing the rebuilt firmware against the digest of the published artifact.

See [Reproducible Builds](/sylica/tutorials/reproducing-builds.md) for the complete verification procedure.

### CI and releases

`.github/workflows/build.yml` builds the supported platform matrix using the isolated build environment.

CI performs the same underlying build used for releases:

```
Git revision
     │
     ▼
platform configuration
     │
     ▼
pinned build environment
     │
     ▼
reproduce/build.sh
     │
     ▼
firmware + checksums
```

Release artifacts include the firmware and the information required to independently verify the build.

The build environment is therefore part of the Sylica release definition rather than an implementation detail of the CI runner.

### Troubleshooting

| Error or symptom                          | Likely cause                                              | Resolution                                                             |
| ----------------------------------------- | --------------------------------------------------------- | ---------------------------------------------------------------------- |
| `unknown platform`                        | No matching platform configuration                        | Check the files under `platforms/`                                     |
| `missing tool: <tool>`                    | Local host dependency is missing                          | Install the corresponding dependency defined in `reproduce/Dockerfile` |
| `edk2 submodules not initialized`         | Required edk2 dependencies are missing                    | Initialize the edk2 submodules                                         |
| Local firmware differs from a release     | Host toolchain differs from the release environment       | Rebuild using the isolated Docker build                                |
| Offline Docker build cannot find an input | Source or dependency was not available before compilation | Check repository and edk2 submodules                                   |
| Reproducibility verification fails        | Two isolated builds produced different firmware           | Compare the build inputs and investigate nondeterministic output       |
