{"id":13339099,"url":"https://github.com/osresearch/safeboot-loader","last_synced_at":"2025-07-07T06:37:13.260Z","repository":{"id":43524126,"uuid":"459206208","full_name":"osresearch/safeboot-loader","owner":"osresearch","description":"Linux kernel module to use UEFI Block IO Protocol devices. Probably not a good idea.","archived":false,"fork":false,"pushed_at":"2022-03-24T09:59:07.000Z","size":446,"stargazers_count":77,"open_issues_count":15,"forks_count":9,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-04-16T00:11:35.144Z","etag":null,"topics":["bootloader","efi","firmware","kernel","uefi"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/osresearch.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-02-14T14:59:37.000Z","updated_at":"2025-02-04T22:17:27.000Z","dependencies_parsed_at":"2022-09-21T04:03:37.678Z","dependency_job_id":null,"html_url":"https://github.com/osresearch/safeboot-loader","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osresearch%2Fsafeboot-loader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osresearch%2Fsafeboot-loader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osresearch%2Fsafeboot-loader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osresearch%2Fsafeboot-loader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/osresearch","download_url":"https://codeload.github.com/osresearch/safeboot-loader/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249173084,"owners_count":21224483,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bootloader","efi","firmware","kernel","uefi"],"created_at":"2024-07-29T19:19:01.241Z","updated_at":"2025-04-16T00:11:42.296Z","avatar_url":"https://github.com/osresearch.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Linux-as-an-EFI-Bootloader\n\n![windows installer chainloaded from Linux](images/chainload.jpg)\n\nThis tree allows Linux to run as an EFI boot loader application\nand to hand control back to the UEFI firmware so that it can chainload\ninto another EFI executable, such as the Windows boot loader.\nIt doesn't touch any devices or UEFI memory, so the firmware state\nis retained upon resuming the UEFI context.\n\n\n## uefidev\n\nThis module provides Linux device driver wrappers for several of the\nUEFI vendor firmwre provided interfaces.  Normally this is not possible\nsince Linux calls `gBS-\u003eExitBootServices()`, which tears down most\nof the UEFI device drivers, and because Linux does not have a memory\nmapping for the UEFI linear memory.\n\nThis module depends on a specially modified loader for the\nkernel that makes the *first* call to `ExitBootServices()` into a NOP,\nand then returns succes.  The loader also allocates memory for the\nkernel at 1 GB and passes this to the kernel with the `memmap=exactmap`\ncommand line option option to ensure that the Linux kernel doesn't\naccidentally modify any of the UEFI data structures.\n\nThe technique of writing directly to CR3 is a total expedient hack\nand definitely not a production ready sort of way to restore the\nmemory map.\n\n\n### Block Devices\n\nThis submodule provides an interface to the vendor firmware's registered\n`EFI_BLOCK_IO_PROTOCOL` handlers, which allows Linux to use them\nas if they were normal block devices.  UEFI tends to create a block\ndevice for the entire disk and then separate ones for each partitions.\nYou can also have Linux detect the partitions by using `losetup` on\nthe whole disk device:\n\n```\nlosetup -f -P /dev/uefi0\nmount /dev/loop0p2 /boot\n```\n\nOr something like this, although your device numbers might be different:\n\n```\nmount -o ro /dev/uefi6 /boot\n```\n\nYou can retrieve the UEFI DevicePath or handle from\n\n```\ncat /sys/devices/virtual/block/uefi6/uefi_devicepath\ncat /sys/devices/virtual/block/uefi6/uefi_handle\n```\n\n\nTodo:\n\n* [ ] Benchmark the performance\n* [X] Test with the ramdisk module\n* [X] Support CDROM devices with their big block sizes\n\n## Ramdisk\n\nThe Linux boot loader can pass data to the next stage via a UEFI\nramdisk, which can be created by echo'ing the disk image file name into\n`/sys/firmware/efi/ramdisk`.\n\n### Loader\n\nNew UEFI modules can be loaded by echo'ing the file name into\n`/sys/firmware/efi/loader`.  This should measure them into\nthe TPM and eventlog.  It can also be used to chain load\nthe next stage, although this won't turn off the Linux interrupts\nand can cause problems.  Use the `chainload` tool instead.\n\n### Network Interfaces\n\nThis submodule create an ethernet interface for each of the\nvendor firmware's registered `EFI_SIMPLE_NETWORK_PROTOCOL` devices.\nThe Linux `skb` transmit functions put packets directly on the wire,\nand there is a periodic timer that polls at 100 Hz for up to ten packets.\nIt's not going to be a fast interface, but it will hopefully be enough\nto perform attestations or other boot time activities.\n\nTodo:\n\n* [ ] Make polling timer a parameter\n* [ ] Interface with the UEFI event system?\n\n\n### TPM Devices\n\nBecause ACPI and PCI are disabled, the TPM is not currently visible\nto Linux via the normal channels.  Instead this submodule will\nquery the `EFI_TCG2_PROTOCOL` objects and create TPM character\ndevices for each of them.  While the UEFI object has methods for\nhigh-level things like \"Extend a PCR and create an event log entry\",\nthis module uses the `SubmitCommand` method to send the raw commands\nthat the Linux driver generates.  It buffers the response and returns\nit immediately; there is no overlapping of commands or multi-threading\nallowed.\n\nTodo:\n\n* [X] Figure out how to expose the TPM.\n* [X] Figure out how to export the TPM event log\n* [X] Change the event log to be \"live\" rather than a copy\n\n## Chainload\n\nThe `chainload` program has a small purgatory to resume the\nUEFI context that the `loader.efi` has stored at 0x100 in physical\nmemory.  It also assumes that the next image to be run is in\nvirtual memory at `0x40100000` and calls `gBS-\u003eLoadImage()` and\nthen `gBS-\u003eStartImage()` on it to transfer control to the\nnew kernel.\n\nTypical usage is:\n\n```\nmount -o ro /dev/uefi6 /boot\nchainload -v --boot-device uefi6 /boot/EFI/Boot/bootx64.efi\n```\n\n\n* [X] Device path for the loaded image is passed in\n\n## Building\n\nThe `Makefile` will download and patch a 5.4.117 kernel with the\nto add the `uefidev` kernel module as an in-tree build option.\nIt will then apply a minimal config that has no PCI drivers and\nuses the EFI framebuffer for video.\n\n```\nmake -j$(nproc)\n```\n\nThis will produce after a while `bootx64.efi` that contains the\nkernel and a minimal initrd, unified with the `loader.efi` program\nusing the same `objcopy` technique as the systemd EFI stub.\nYou can sign it with `sbsigntool` for SecureBoot systems or\nboot it without signing on qemu.\n\n\nCreating the local machine state (not including the TPM):\n```\nqemu-img create win10.hda 12G\n```\n\n### Running in QEMU\n\nLaunching the emulator is messy due to the need to pass in the separate\nUEFI nvram (the one in `config/OVMF_VARS.fd` has been modified so that\nthe UEFI boot order starts with PXE; otherwise the virtual machine will\nalways boot via the hard disk since OVMF ignores the `-boot n` option\nto request a network boot).\n\nYou can run `make qemu` or:\n\n```\nqemu-system-x86_64 \\\n  -M q35,accel=kvm \\\n  -m 2G \\\n  -drive if=pflash,format=raw,readonly,file=/usr/share/OVMF/OVMF_CODE.fd \\\n  -drive if=pflash,format=raw,file=config/OVMF_VARS.fd \\\n  -netdev user,id=eth0,tftp=.,bootfile=build/bootx64.efi \\\n  -device e1000,netdev=eth0 \\\n  -serial stdio \\\n  -cdrom win10.iso \\\n  -hda win10.img\n```\n\n### TPM emulation\n\nTo emulate a TPM 2.0, install or build the `swtpm` package and \nrun with `make TPM=1 qemu`.  The TPM's EK will be in `build/tpm-state/ek.pem`\nfor attestation verification.\n\nTested with:\n\n* [swtpm 0.7.1](https://github.com/stefanberger/swtpm/releases/tag/v0.7.1)\n* [libtpms 0.9.1](https://github.com/stefanberger/libtpms/releases/tag/v0.9.1)\n* [tpm2-tss 3.2.0](https://github.com/tpm2-software/tpm2-tss/releases/tag/3.2.0)\n* [tpm2-tools 5.2](https://github.com/tpm2-software/tpm2-tools/releases/tag/5.2)\n\n### Debugging\n\nFor more convenient debugging, you can turn off the graphical QEMU window:\n`make NOGRAPHIC=1 qemu`\n\n### Todo\n\n* [X] Wrap kernel building in the `Makefile`\n* [X] `initrd.cpio` building\n* [X] Unified image building with the loader\n* [ ] LinuxKit or buildroot integration?\n\n### Kernel command line\n\n```\nmemmap=exactmap,32K@0G,512M@1G noefi acpi=off\n```\n\n* [ ] Make the loader built this addition to the command line\n* [ ] Allocate the SMP trampoline correclty in UEFI\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fosresearch%2Fsafeboot-loader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fosresearch%2Fsafeboot-loader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fosresearch%2Fsafeboot-loader/lists"}