├── .gitignore ├── Makefile ├── README.md ├── chainload ├── Makefile ├── README.md ├── boot.sh ├── chainload.c ├── chainload.h ├── efi-chainload.c ├── efi-entry.c ├── loader.c ├── pe.c ├── pe.h ├── resume.h └── unify-kernel ├── config ├── OVMF_VARS.fd ├── RamDiskDxe.efi ├── cmdline-5.4.117.txt ├── linux-5.4.117.config └── linux-5.4.117.patch ├── images └── chainload.jpg ├── initrd ├── Makefile ├── attest-boot ├── cpio-clean ├── files.txt ├── init ├── populate └── ramdisk-create ├── kernel ├── Makefile └── dev.cpio ├── module ├── Kconfig ├── Makefile ├── Tcg2Protocol.h ├── blockio.c ├── blockio.h ├── efidhcp4.h ├── efinet.c ├── efinet.h ├── efiwrapper.c ├── efiwrapper.h ├── event.c ├── loader.c ├── main.c ├── ramdisk.c ├── ramdisk.h └── tpm.c └── tpm.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | .*.cmd 3 | Module.symvers 4 | *.o 5 | *.a 6 | modules.order 7 | *.ko 8 | *.mod 9 | *.mod.c 10 | build 11 | kernel/linux* 12 | *.sign 13 | *.xz 14 | .*.d 15 | tags 16 | ctags 17 | *.bin 18 | *.efi 19 | *.exe 20 | *.img 21 | *.iso 22 | *.fd 23 | *.tar.gz 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | O ?= ./build 2 | TPM_DIR ?= $O/tpm-state 3 | 4 | all: $O/bootx64.efi | $O 5 | 6 | $O: 7 | mkdir -p $@ 8 | 9 | clean: 10 | rm -rf $O/chainload 11 | rm -rf $O/initrd* 12 | 13 | $O/bootx64.efi: $O/chainload/loader.efi $O/vmlinuz $O/initrd.cpio.xz 14 | $O/chainload/unify-kernel $@ \ 15 | linux=$O/vmlinuz \ 16 | initrd=$O/initrd.cpio.xz \ 17 | cmdline=config/cmdline-5.4.117.txt 18 | 19 | $O/vmlinuz: FORCE 20 | $(MAKE) -C kernel 21 | module/uefidev.ko: build/vmlinuz FORCE 22 | $(MAKE) -C $(dir $@) 23 | $O/chainload/loader.efi: build/chainload/chainload 24 | $O/chainload/chainload: FORCE 25 | $(MAKE) -C chainload 26 | $O/initrd.cpio.xz: build/chainload/chainload FORCE 27 | $(MAKE) -C initrd 28 | 29 | # Arguments/parameters for QEMU, allowing for customization through make vars 30 | _OVMF := /usr/share/OVMF/OVMF_CODE.fd 31 | _OVMFSTATE := $O/OVMF_VARS.fd 32 | _BOOT := ,tftp=.,bootfile=$O/bootx64.efi 33 | _NET := -netdev user,id=eth0$(_BOOT) -device e1000,netdev=eth0 34 | _WIN ?= -hda win10.img 35 | _CDROM ?= -cdrom win10.iso 36 | 37 | # see https://qemu-project.gitlab.io/qemu/specs/tpm.html 38 | _TPM := \ 39 | -chardev socket,id=chrtpm,path="$(TPM_DIR)/swtpm-sock.ctrl" \ 40 | -tpmdev emulator,id=tpm0,chardev=chrtpm \ 41 | -device tpm-tis,tpmdev=tpm0 \ 42 | $(if $(TPMSTATE), -incoming "exec:cat < testvm.bin" ) \ 43 | 44 | 45 | # Extra arguments and optional changes for QEMU invocation 46 | _QEMU_ARGS := \ 47 | $(_NET) \ 48 | $(_WIN) \ 49 | $(_CDROM) \ 50 | $(if $(TPM),$(_TPM)) \ 51 | $(if $(NOGRAPHIC), -nographic -monitor /dev/null, -vga std ) \ 52 | 53 | # Copy the clean net-boot OVMF state to the build directory 54 | $(_OVMFSTATE): config/OVMF_VARS.fd 55 | cp $< $@ 56 | 57 | qemu: $O/bootx64.efi $(_OVMFSTATE) 58 | $(if $(TPM), ./tpm.sh "$(TPM_DIR)" ) 59 | qemu-system-x86_64 \ 60 | -M q35,accel=kvm \ 61 | -cpu host \ 62 | -m 2G \ 63 | -serial stdio \ 64 | -drive if=pflash,format=raw,readonly=on,file=$(_OVMF) \ 65 | -drive if=pflash,format=raw,file=$(_OVMFSTATE) \ 66 | $(_QEMU_ARGS) \ 67 | 68 | 69 | FORCE: 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Linux-as-an-EFI-Bootloader 2 | 3 | ![windows installer chainloaded from Linux](images/chainload.jpg) 4 | 5 | This tree allows Linux to run as an EFI boot loader application 6 | and to hand control back to the UEFI firmware so that it can chainload 7 | into another EFI executable, such as the Windows boot loader. 8 | It doesn't touch any devices or UEFI memory, so the firmware state 9 | is retained upon resuming the UEFI context. 10 | 11 | 12 | ## uefidev 13 | 14 | This module provides Linux device driver wrappers for several of the 15 | UEFI vendor firmwre provided interfaces. Normally this is not possible 16 | since Linux calls `gBS->ExitBootServices()`, which tears down most 17 | of the UEFI device drivers, and because Linux does not have a memory 18 | mapping for the UEFI linear memory. 19 | 20 | This module depends on a specially modified loader for the 21 | kernel that makes the *first* call to `ExitBootServices()` into a NOP, 22 | and then returns succes. The loader also allocates memory for the 23 | kernel at 1 GB and passes this to the kernel with the `memmap=exactmap` 24 | command line option option to ensure that the Linux kernel doesn't 25 | accidentally modify any of the UEFI data structures. 26 | 27 | The technique of writing directly to CR3 is a total expedient hack 28 | and definitely not a production ready sort of way to restore the 29 | memory map. 30 | 31 | 32 | ### Block Devices 33 | 34 | This submodule provides an interface to the vendor firmware's registered 35 | `EFI_BLOCK_IO_PROTOCOL` handlers, which allows Linux to use them 36 | as if they were normal block devices. UEFI tends to create a block 37 | device for the entire disk and then separate ones for each partitions. 38 | You can also have Linux detect the partitions by using `losetup` on 39 | the whole disk device: 40 | 41 | ``` 42 | losetup -f -P /dev/uefi0 43 | mount /dev/loop0p2 /boot 44 | ``` 45 | 46 | Or something like this, although your device numbers might be different: 47 | 48 | ``` 49 | mount -o ro /dev/uefi6 /boot 50 | ``` 51 | 52 | You can retrieve the UEFI DevicePath or handle from 53 | 54 | ``` 55 | cat /sys/devices/virtual/block/uefi6/uefi_devicepath 56 | cat /sys/devices/virtual/block/uefi6/uefi_handle 57 | ``` 58 | 59 | 60 | Todo: 61 | 62 | * [ ] Benchmark the performance 63 | * [X] Test with the ramdisk module 64 | * [X] Support CDROM devices with their big block sizes 65 | 66 | ## Ramdisk 67 | 68 | The Linux boot loader can pass data to the next stage via a UEFI 69 | ramdisk, which can be created by echo'ing the disk image file name into 70 | `/sys/firmware/efi/ramdisk`. 71 | 72 | ### Loader 73 | 74 | New UEFI modules can be loaded by echo'ing the file name into 75 | `/sys/firmware/efi/loader`. This should measure them into 76 | the TPM and eventlog. It can also be used to chain load 77 | the next stage, although this won't turn off the Linux interrupts 78 | and can cause problems. Use the `chainload` tool instead. 79 | 80 | ### Network Interfaces 81 | 82 | This submodule create an ethernet interface for each of the 83 | vendor firmware's registered `EFI_SIMPLE_NETWORK_PROTOCOL` devices. 84 | The Linux `skb` transmit functions put packets directly on the wire, 85 | and there is a periodic timer that polls at 100 Hz for up to ten packets. 86 | It's not going to be a fast interface, but it will hopefully be enough 87 | to perform attestations or other boot time activities. 88 | 89 | Todo: 90 | 91 | * [ ] Make polling timer a parameter 92 | * [ ] Interface with the UEFI event system? 93 | 94 | 95 | ### TPM Devices 96 | 97 | Because ACPI and PCI are disabled, the TPM is not currently visible 98 | to Linux via the normal channels. Instead this submodule will 99 | query the `EFI_TCG2_PROTOCOL` objects and create TPM character 100 | devices for each of them. While the UEFI object has methods for 101 | high-level things like "Extend a PCR and create an event log entry", 102 | this module uses the `SubmitCommand` method to send the raw commands 103 | that the Linux driver generates. It buffers the response and returns 104 | it immediately; there is no overlapping of commands or multi-threading 105 | allowed. 106 | 107 | Todo: 108 | 109 | * [X] Figure out how to expose the TPM. 110 | * [X] Figure out how to export the TPM event log 111 | * [X] Change the event log to be "live" rather than a copy 112 | 113 | ## Chainload 114 | 115 | The `chainload` program has a small purgatory to resume the 116 | UEFI context that the `loader.efi` has stored at 0x100 in physical 117 | memory. It also assumes that the next image to be run is in 118 | virtual memory at `0x40100000` and calls `gBS->LoadImage()` and 119 | then `gBS->StartImage()` on it to transfer control to the 120 | new kernel. 121 | 122 | Typical usage is: 123 | 124 | ``` 125 | mount -o ro /dev/uefi6 /boot 126 | chainload -v --boot-device uefi6 /boot/EFI/Boot/bootx64.efi 127 | ``` 128 | 129 | 130 | * [X] Device path for the loaded image is passed in 131 | 132 | ## Building 133 | 134 | The `Makefile` will download and patch a 5.4.117 kernel with the 135 | to add the `uefidev` kernel module as an in-tree build option. 136 | It will then apply a minimal config that has no PCI drivers and 137 | uses the EFI framebuffer for video. 138 | 139 | ``` 140 | make -j$(nproc) 141 | ``` 142 | 143 | This will produce after a while `bootx64.efi` that contains the 144 | kernel and a minimal initrd, unified with the `loader.efi` program 145 | using the same `objcopy` technique as the systemd EFI stub. 146 | You can sign it with `sbsigntool` for SecureBoot systems or 147 | boot it without signing on qemu. 148 | 149 | 150 | Creating the local machine state (not including the TPM): 151 | ``` 152 | qemu-img create win10.hda 12G 153 | ``` 154 | 155 | ### Running in QEMU 156 | 157 | Launching the emulator is messy due to the need to pass in the separate 158 | UEFI nvram (the one in `config/OVMF_VARS.fd` has been modified so that 159 | the UEFI boot order starts with PXE; otherwise the virtual machine will 160 | always boot via the hard disk since OVMF ignores the `-boot n` option 161 | to request a network boot). 162 | 163 | You can run `make qemu` or: 164 | 165 | ``` 166 | qemu-system-x86_64 \ 167 | -M q35,accel=kvm \ 168 | -m 2G \ 169 | -drive if=pflash,format=raw,readonly,file=/usr/share/OVMF/OVMF_CODE.fd \ 170 | -drive if=pflash,format=raw,file=config/OVMF_VARS.fd \ 171 | -netdev user,id=eth0,tftp=.,bootfile=build/bootx64.efi \ 172 | -device e1000,netdev=eth0 \ 173 | -serial stdio \ 174 | -cdrom win10.iso \ 175 | -hda win10.img 176 | ``` 177 | 178 | ### TPM emulation 179 | 180 | To emulate a TPM 2.0, install or build the `swtpm` package and 181 | run with `make TPM=1 qemu`. The TPM's EK will be in `build/tpm-state/ek.pem` 182 | for attestation verification. 183 | 184 | Tested with: 185 | 186 | * [swtpm 0.7.1](https://github.com/stefanberger/swtpm/releases/tag/v0.7.1) 187 | * [libtpms 0.9.1](https://github.com/stefanberger/libtpms/releases/tag/v0.9.1) 188 | * [tpm2-tss 3.2.0](https://github.com/tpm2-software/tpm2-tss/releases/tag/3.2.0) 189 | * [tpm2-tools 5.2](https://github.com/tpm2-software/tpm2-tools/releases/tag/5.2) 190 | 191 | ### Debugging 192 | 193 | For more convenient debugging, you can turn off the graphical QEMU window: 194 | `make NOGRAPHIC=1 qemu` 195 | 196 | ### Todo 197 | 198 | * [X] Wrap kernel building in the `Makefile` 199 | * [X] `initrd.cpio` building 200 | * [X] Unified image building with the loader 201 | * [ ] LinuxKit or buildroot integration? 202 | 203 | ### Kernel command line 204 | 205 | ``` 206 | memmap=exactmap,32K@0G,512M@1G noefi acpi=off 207 | ``` 208 | 209 | * [ ] Make the loader built this addition to the command line 210 | * [ ] Allocate the SMP trampoline correclty in UEFI 211 | -------------------------------------------------------------------------------- /chainload/Makefile: -------------------------------------------------------------------------------- 1 | O ?= ../build/chainload 2 | 3 | all: $O/chainload $O/loader.efi $O/unify-kernel 4 | 5 | 6 | $O: 7 | mkdir -p $O 8 | 9 | CFLAGS = \ 10 | -O3 \ 11 | -W \ 12 | -Wall \ 13 | -g \ 14 | -m64 \ 15 | -MMD \ 16 | -MF $O/.$(notdir $@).d \ 17 | 18 | EFI_CFLAGS = \ 19 | $(CFLAGS) \ 20 | -D__efi__ \ 21 | -DGNU_EFI_USE_MS_ABI \ 22 | -fshort-wchar \ 23 | -mno-red-zone \ 24 | -fno-stack-protector \ 25 | -I /usr/include/efi \ 26 | -I /usr/include/efi/x86_64 \ 27 | -fpic \ 28 | -static \ 29 | -nostdlib \ 30 | $(EXTRA_CFLAGS) 31 | 32 | LDFLAGS = \ 33 | -fpic \ 34 | -nostdlib \ 35 | -znocombreloc \ 36 | 37 | LDFLAGS.dynamic = \ 38 | -shared \ 39 | -Bsymbolic \ 40 | 41 | 42 | $O/unify-kernel: unify-kernel | $O 43 | cp $< $@ 44 | 45 | $O/chainload: chainload.c $O/chainload.bin | $O 46 | $(CC) $(CFLAGS) \ 47 | -DCHAINLOAD_BIN="\"$O/chainload.bin\"" \ 48 | -o $@ \ 49 | $< 50 | 51 | # This is not really an EFI application, so it overrides the entry point 52 | $O/chainload.rawefi: $O/efi-entry.o $O/efi-chainload.o | $O 53 | $O/chainload.rawefi: EXTRA_CFLAGS=-Wl,-eentry 54 | 55 | # This is really an EFI application, so it uses the full libraries 56 | $O/loader.exe: $O/loader.o $O/pe.o 57 | 58 | $O/%.o: %.c | $O 59 | $(CC) $(EFI_CFLAGS) \ 60 | -c \ 61 | -o $@ \ 62 | $< \ 63 | 64 | $O/%.rawefi: 65 | $(CC) $(EFI_CFLAGS) \ 66 | -o $@ \ 67 | $^ \ 68 | 69 | $O/%.bin: $O/%.rawefi 70 | objcopy \ 71 | -O binary \ 72 | -j .text \ 73 | -j .rodata \ 74 | $< \ 75 | $@ 76 | 77 | $O/%.exe: 78 | ld \ 79 | $(LDFLAGS) \ 80 | $(LDFLAGS.dynamic) \ 81 | -o $@ \ 82 | -T /usr/lib/elf_x86_64_efi.lds \ 83 | /usr/lib/crt0-efi-x86_64.o \ 84 | $^ \ 85 | -L /usr/lib \ 86 | -l:libgnuefi.a \ 87 | -l:libefi.a \ 88 | 89 | $O/%.efi: $O/%.exe 90 | objcopy \ 91 | -j .text \ 92 | -j .sdata \ 93 | -j .data \ 94 | -j .dynamic \ 95 | -j .dynsym \ 96 | -j .rel \ 97 | -j .rela \ 98 | -j .reloc \ 99 | --target efi-app-x86_64 \ 100 | $^ \ 101 | $@ 102 | 103 | clean: FORCE 104 | $(RM) $O/*.efi $O/*.bin $O/*.o $O/*.exe $O/.*.d $O/*.rawefi 105 | FORCE: 106 | 107 | -include $O/.*.d 108 | -------------------------------------------------------------------------------- /chainload/README.md: -------------------------------------------------------------------------------- 1 | # Chainload from Linux to Windows 2 | 3 | This has two important pieces for the Linux-as-a-bootloader to be able 4 | to boot Windows: 5 | 6 | * An EFI stub that saves UEFI context and prevents `ExitBootServices` from being called 7 | * The `chainload` tool that will pass control back to UEFI and invoke a new image. 8 | 9 | ## UEFI Stub for Linux Boot Loader 10 | 11 | This is a special EFI wrapper for the Linux kernel, initrd and command line 12 | is used that saves the UEFI context into low memory and allocates 13 | a range of contiguous memory for the kernel. This context 14 | is restored following a `kexec_load()` call in the kernel, which 15 | looks to the UEFI firmware as if the wrapper has returned. 16 | 17 | The Linux kernel must not disturb devices or touch memory that 18 | UEFI is using, so it is started with the `memmap` kernel commandline 19 | parameter to restrict it to the allocated chunk. The kernel also 20 | has to be told not to interact with ACPI, EFI and PCI. 21 | 22 | ``` 23 | memmap=exactmap,32K@0G,512M@1G noefi 24 | ``` 25 | 26 | The 32KiB at 0x0 is necessary for the SMP trampoline, even with 27 | a non-SMP kernel. 28 | 29 | TODO: Have the stub add this parameter based on its allocation. 30 | 31 | The memory is allocated at 1 GB so that the first page table entry is 32 | free and all of the UEFI memory can be restored by replacing the first 33 | entry in the table with the stored value from the UEFI CR3. 34 | 35 | 36 | ## Chainload 37 | 38 | This tool retrieves the stored context and the image to be invoked, then 39 | wraps the `kexec_load()` system call to shutdown the Linux devices, restore 40 | the UEFI context, and then invoke `gBS->LoadImage()` and `gBS->StartImage()` 41 | on the next boot loader. 42 | 43 | If things fail, this *should* return to UEFI and maybe invoke the shell or 44 | the `BootNext` behaviour. 45 | -------------------------------------------------------------------------------- /chainload/boot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | CHAINLOAD="$1" 3 | if [ -z "$CHAINLOAD" ]; then 4 | CHAINLOAD=/boot/EFI/Boot/bootx64.efi 5 | fi 6 | 7 | echo -n /bin/test.gpt > /sys/firmware/efi/ramdisk 8 | 9 | sleep 1 10 | if [ ! -r /dev/uefi7 ]; then 11 | echo "no uefi device yet?" 12 | sleep 5 13 | if [ ! -r /dev/uefi7 ]; then 14 | echo "no really, no uefi device" 15 | exit 1 16 | fi 17 | fi 18 | 19 | mount /dev/uefi7 /boot 20 | 21 | if [ ! -r "$CHAINLOAD" ]; then 22 | echo "$CHAINLOAD: not present?" 23 | exit 1 24 | fi 25 | 26 | chainload -v "$CHAINLOAD" 27 | 28 | -------------------------------------------------------------------------------- /chainload/chainload.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Chainload from LinuxBoot into another UEFI executable. 3 | * 4 | * This uses the kexec_load system call to allow arbitrary files and 5 | * segments to be passed into the new kernel image. 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "chainload.h" 24 | #include "resume.h" 25 | 26 | /* 27 | * The kexec_load() sysmtel call is not in any header, so we must 28 | * create a fake entry for it. 29 | 30 | struct kexec_segment { 31 | void *buf; // Buffer in user space 32 | size_t bufsz; // Buffer length in user space 33 | void *mem; // Physical address of kernel 34 | size_t memsz; // Physical address length 35 | }; 36 | */ 37 | 38 | long kexec_load( 39 | unsigned long entry, 40 | unsigned long nr_segments, 41 | struct kexec_segment *segments, 42 | unsigned long flags 43 | ) 44 | { 45 | return syscall(__NR_kexec_load, entry, nr_segments, segments, flags); 46 | } 47 | 48 | 49 | /* 50 | * Import the pre-compiled purgatory that will hand off control 51 | * via the UEFI boot services LoadImageProtocol. 52 | */ 53 | 54 | #ifndef CHAINLOAD_BIN 55 | #error "CHAINLOAD_BIN is not defined" 56 | #endif 57 | 58 | asm( 59 | ".globl _purgatory;" 60 | "_purgatory:\n" 61 | ".incbin \"" CHAINLOAD_BIN "\";" 62 | ".globl _purgatory_end;" 63 | "_purgatory_end:\n" 64 | ); 65 | extern const char _purgatory, _purgatory_end; 66 | 67 | 68 | // TODO: where is this defined? 69 | #define PAGESIZE (4096uL) 70 | #define PAGE_ROUND(x) (((x) + PAGESIZE - 1) & ~(PAGESIZE-1)) 71 | 72 | static int verbose = 0; 73 | 74 | static void * read_file(const char * filename, size_t * size_out) 75 | { 76 | const int fd = open(filename, O_RDONLY); 77 | if (fd < 0) 78 | goto fail_open; 79 | 80 | struct stat statbuf; 81 | if (fstat(fd, &statbuf) < 0) 82 | goto fail_stat; 83 | 84 | size_t size = statbuf.st_size; 85 | 86 | if (size_out && *size_out != 0) 87 | { 88 | // limit the size to the provided size 89 | // or if the file is a device (such as /dev/mem) 90 | if (size > *size_out || (statbuf.st_mode & S_IFCHR) != 0) 91 | size = *size_out; 92 | } 93 | 94 | if (verbose) 95 | fprintf(stderr, "%s: %zu bytes\n", filename, size); 96 | 97 | uint8_t * buf = malloc(size); 98 | if (!buf) 99 | goto fail_malloc; 100 | 101 | size_t off = 0; 102 | while(off < size) 103 | { 104 | ssize_t rc = read(fd, buf + off, size - off); 105 | if (rc < 0) 106 | { 107 | if (errno == EINTR || errno == EAGAIN) 108 | continue; 109 | goto fail_read; 110 | } 111 | 112 | off += rc; 113 | } 114 | 115 | 116 | if (size_out) 117 | *size_out = size; 118 | 119 | return buf; 120 | 121 | fail_read: 122 | free(buf); 123 | fail_malloc: 124 | fail_stat: 125 | close(fd); 126 | fail_open: 127 | return NULL; 128 | } 129 | 130 | 131 | static const char usage[] = 132 | "Usage: chainload [options] /boot/EFI/Boot/bootx64.efi\n" 133 | "\n" 134 | "Options:\n" 135 | "-h | --help This help\n" 136 | "-v | --verbose Print debug info\n" 137 | "-d | --boot-device uefiN Boot device for image device path\n" 138 | "-r | --no-reboot Do not execute final kexec call\n" 139 | "-p | --purgatory file.bin Chainload purgatory (defaults to builtin)\n" 140 | "-c | --context file.bin UEFI context (defaults to /dev/mem)\n" 141 | "-e | --entry 0x40000000 Entry point for the new universe\n" 142 | "\n" 143 | "The entry point must match the UEFI allocated memory, which is\n" 144 | "passed via memmap=exactmap on the kernel command line.\n" 145 | "\n" 146 | ""; 147 | 148 | static const struct option options[] = { 149 | { "help", no_argument, 0, 'h' }, 150 | { "verbose", no_argument, 0, 'v' }, 151 | { "no-reboot", no_argument, 0, 'r' }, 152 | { "boot-device", required_argument, 0, 'd' }, 153 | { "purgatory", required_argument, 0, 'p' }, 154 | { "context", required_argument, 0, 'c' }, 155 | { "entry", required_argument, 0, 'e' }, 156 | { 0, 0, 0, 0}, 157 | }; 158 | 159 | 160 | int main(int argc, char ** argv) 161 | { 162 | unsigned long flags = KEXEC_ARCH_DEFAULT; 163 | struct kexec_segment segments[KEXEC_SEGMENT_MAX]; 164 | 165 | int do_not_reboot = 0; 166 | const char * context_file = "/dev/mem"; 167 | const char * purgatory_file = NULL; 168 | const char * filesystem_str = NULL; 169 | uint64_t entry_point = 0x40000000; // todo: figure out automatically 170 | 171 | opterr = 1; 172 | optind = 1; 173 | 174 | while (1) 175 | { 176 | const int opt = getopt_long(argc, argv, "h?d:p:c:e:vr", options, 0); 177 | if (opt < 0) 178 | break; 179 | 180 | switch(opt) { 181 | case 'd': 182 | filesystem_str = optarg; 183 | break; 184 | case 'p': 185 | purgatory_file = optarg; 186 | break; 187 | case 'c': 188 | context_file = optarg; 189 | break; 190 | case 'e': 191 | entry_point = strtoul(optarg, NULL, 0); 192 | break; 193 | case 'v': 194 | verbose++; 195 | break; 196 | case 'r': 197 | do_not_reboot = 1; 198 | break; 199 | case '?': case 'h': 200 | printf("%s", usage); 201 | return EXIT_FAILURE; 202 | default: 203 | fprintf(stderr, "%s", usage); 204 | return EXIT_FAILURE; 205 | } 206 | } 207 | 208 | if (argc - optind < 1) 209 | { 210 | fprintf(stderr, "Missing EFI Executable to chainload!\n"); 211 | return EXIT_FAILURE; 212 | } 213 | 214 | size_t exe_size = 0; // unlimited 215 | const char * exe_file = argv[optind]; 216 | const void * exe_image = read_file(exe_file, &exe_size); 217 | if (exe_image == NULL) 218 | { 219 | perror(exe_file); 220 | return EXIT_FAILURE; 221 | } 222 | 223 | size_t context_size = PAGESIZE; 224 | const void * context_image = read_file(context_file, &context_size); 225 | if (context_image == NULL) 226 | { 227 | perror(context_file); 228 | return EXIT_FAILURE; 229 | } 230 | 231 | const void * purgatory_image = &_purgatory; 232 | size_t purgatory_size = &_purgatory_end - &_purgatory; 233 | 234 | if (purgatory_file) 235 | { 236 | // allow a larger puragtory 237 | purgatory_size = 65536; 238 | purgatory_image = read_file(purgatory_file, &purgatory_size); 239 | if (!purgatory_image) 240 | { 241 | perror(purgatory_file); 242 | return EXIT_FAILURE; 243 | } 244 | } 245 | 246 | uint64_t boot_device = 0; 247 | if (filesystem_str) 248 | { 249 | char dev_str[256]; 250 | snprintf(dev_str, sizeof(dev_str), "/sys/devices/virtual/block/%s/uefi_handle", filesystem_str); 251 | FILE * f = fopen(dev_str, "r"); 252 | if (!f) 253 | { 254 | perror(dev_str); 255 | return EXIT_FAILURE; 256 | } 257 | 258 | if (fscanf(f, "%"PRIx64, &boot_device) != 1) 259 | { 260 | fprintf(stderr, "%s: failed to read boot device handle\n", filesystem_str); 261 | return EXIT_FAILURE; 262 | } 263 | 264 | fclose(f); 265 | 266 | if (verbose) 267 | printf("%s: handle 0x%"PRIx64"\n", 268 | dev_str, boot_device); 269 | } 270 | 271 | int num_segments = 0; 272 | uint64_t exe_addr = entry_point + CHAINLOAD_IMAGE_OFFSET; 273 | 274 | uefi_context_t * context 275 | = (void*)(UEFI_CONTEXT_OFFSET + (uint8_t*) context_image); 276 | 277 | if (context->magic != UEFI_CONTEXT_MAGIC) 278 | fprintf(stderr, "WARNING: context magic %016lx does not have expected magic %016lx\n", 279 | context->magic, UEFI_CONTEXT_MAGIC); 280 | 281 | if (verbose) 282 | printf("context: CR3=%p gST=%p\n", 283 | (void*) context->cr3, (void*) context->system_table); 284 | 285 | // poke the entry point into the saved context so that 286 | // the new universe can find our argument block 287 | context->chainload_ptr = entry_point + CHAINLOAD_ARGS_OFFSET; 288 | 289 | chainload_args_t chainload_args = { 290 | .magic = CHAINLOAD_ARGS_MAGIC, 291 | .image_addr = exe_addr, 292 | .image_size = exe_size, 293 | .boot_device = boot_device, 294 | }; 295 | 296 | segments[num_segments++] = (struct kexec_segment){ 297 | .buf = context_image, 298 | .bufsz = context_size, 299 | .mem = (const void*) 0x00000000, // could go elsewhere 300 | .memsz = PAGE_ROUND(context_size), 301 | }; 302 | 303 | segments[num_segments++] = (struct kexec_segment){ 304 | .buf = purgatory_image, 305 | .bufsz = purgatory_size, 306 | .mem = (const void*) entry_point, 307 | .memsz = PAGE_ROUND(purgatory_size), 308 | }; 309 | 310 | segments[num_segments++] = (struct kexec_segment){ 311 | .buf = exe_image, 312 | .bufsz = exe_size, 313 | .mem = (const void*) exe_addr, 314 | .memsz = PAGE_ROUND(exe_size), 315 | }; 316 | 317 | segments[num_segments++] = (struct kexec_segment){ 318 | .buf = &chainload_args, 319 | .bufsz = sizeof(chainload_args), 320 | .mem = (void*) context->chainload_ptr, 321 | .memsz = PAGE_ROUND(sizeof(chainload_args)), 322 | }; 323 | 324 | if(verbose) 325 | for(int i = 0 ; i < num_segments ; i++) 326 | { 327 | const struct kexec_segment * seg = &segments[i]; 328 | printf("%d: %p + %zu => %p + %zu\n", 329 | i, 330 | (const void*) seg->buf, 331 | (size_t) seg->bufsz, 332 | (const void*) seg->mem, 333 | (size_t) seg->memsz 334 | ); 335 | } 336 | 337 | 338 | int rc = kexec_load(entry_point, num_segments, segments, flags); 339 | if (rc < 0) 340 | { 341 | perror("kexec_load"); 342 | return EXIT_FAILURE; 343 | } 344 | 345 | if (do_not_reboot) 346 | return EXIT_SUCCESS; 347 | 348 | if (verbose) 349 | fprintf(stderr, "kexec-load: rebooting\n"); 350 | 351 | rc = reboot(LINUX_REBOOT_CMD_KEXEC); 352 | if (rc < 0) 353 | { 354 | perror("reboot"); 355 | return EXIT_FAILURE; 356 | } 357 | 358 | printf("kexec-load: we shouldn't be here...\n"); 359 | return EXIT_SUCCESS; // ??? 360 | } 361 | -------------------------------------------------------------------------------- /chainload/chainload.h: -------------------------------------------------------------------------------- 1 | #ifndef _uefidev_chainload_h_ 2 | #define _uefidev_chainload_h_ 3 | 4 | #include 5 | 6 | #define CHAINLOAD_IMAGE_OFFSET 0x20000 7 | #define CHAINLOAD_ARGS_OFFSET 0x10000 8 | #define CHAINLOAD_ARGS_MAGIC 0x434841494e4c4432uL 9 | 10 | typedef struct { 11 | uint64_t magic; 12 | uint64_t boot_device; // device handle 13 | uint64_t image_addr; // physical address 14 | uint64_t image_size; 15 | } chainload_args_t; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /chainload/efi-chainload.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Chainload into a new UEFI executable. 3 | * 4 | * This is sort-of-UEFI code; it runs on the bare metal after 5 | * the kexec_load() has transfered control to the entry point, 6 | * which restored UEFI context. 7 | * 8 | * It links against the gnuefi library and uses the sysV ABI, 9 | * and calls into UEFI MS ABI functions (RCX, RDX, R8, R9). 10 | * 11 | * You're not expected to run this directly; it will be linked 12 | * into the Linux chainload tool and then passed via kexec_load 13 | * to the new universe. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include "chainload.h" 20 | #include "resume.h" 21 | 22 | #ifdef CONFIG_EFILIB 23 | #include 24 | #else 25 | static EFI_BOOT_SERVICES * gBS; 26 | #endif 27 | 28 | static EFI_DEVICE_PATH_PROTOCOL * find_boot_device(uint64_t handle) 29 | { 30 | EFI_GUID devpath_guid = EFI_DEVICE_PATH_PROTOCOL_GUID; 31 | EFI_DEVICE_PATH_PROTOCOL* boot_device = NULL; 32 | 33 | if (handle == 0) 34 | return NULL; 35 | 36 | int status = gBS->HandleProtocol( 37 | (EFI_HANDLE) handle, 38 | &devpath_guid, 39 | (VOID**)&boot_device); 40 | if (status != 0) 41 | return NULL; 42 | 43 | return boot_device; 44 | } 45 | 46 | static CHAR16 * devpath2txt(EFI_DEVICE_PATH_PROTOCOL * dp) 47 | { 48 | EFI_HANDLE handles[64]; 49 | UINTN handlebufsz = sizeof(handles); 50 | EFI_GUID devpath2txt_guid = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID; 51 | 52 | int status = gBS->LocateHandle( 53 | ByProtocol, 54 | &devpath2txt_guid, 55 | NULL, 56 | &handlebufsz, 57 | handles); 58 | if (status != 0) 59 | return u"NO-PROTOCOL"; 60 | 61 | // Now we must loop through every handle returned, and open it up 62 | UINTN num_handles = handlebufsz / sizeof(EFI_HANDLE); 63 | if (num_handles < 1) 64 | return u"NO-HANDLES"; 65 | 66 | EFI_DEVICE_PATH_TO_TEXT_PROTOCOL * dp2txt = NULL; 67 | 68 | status = gBS->HandleProtocol( 69 | handles[0], 70 | &devpath2txt_guid, 71 | (void**) &dp2txt); 72 | if (status != 0) 73 | return u"NO-HANDLER"; 74 | 75 | return dp2txt->ConvertDevicePathToText(dp, 0, 0); 76 | } 77 | 78 | 79 | int 80 | efi_entry(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE * const ST) 81 | { 82 | ST->ConOut->OutputString(ST->ConOut, u"chainload says hello\r\n"); 83 | 84 | #ifdef CONFIG_EFILIB 85 | InitializeLib(image_handle, ST); 86 | #endif 87 | gBS = ST->BootServices; 88 | 89 | const chainload_args_t * args = (void*) UEFI_CONTEXT->chainload_ptr; 90 | 91 | if (args->magic != CHAINLOAD_ARGS_MAGIC) 92 | #ifdef CONFIG_EFILIB 93 | Print(u"chainload wrong magic %x != %x\r\n", 94 | args->magic, CHAINLOAD_ARGS_MAGIC); 95 | #else 96 | ST->ConOut->OutputString(ST->ConOut, u"chainload wrong magic\r\n"); 97 | #endif 98 | 99 | 100 | // let's find the path to the boot device 101 | // for now we hard code it as the second filesystem device 102 | EFI_DEVICE_PATH_PROTOCOL * boot_device = find_boot_device(args->boot_device); 103 | 104 | CHAR16 * boot_path = boot_device ? devpath2txt(boot_device) : u"NONE"; 105 | #ifdef CONFIG_EFILIB 106 | Print(u"Boot device %d: %s\r\n", args->boot_device, boot_path); 107 | #else 108 | ST->ConOut->OutputString(ST->ConOut, u"Boot device "); 109 | ST->ConOut->OutputString(ST->ConOut, boot_path); 110 | ST->ConOut->OutputString(ST->ConOut, u"\r\n"); 111 | #endif 112 | 113 | // let's try to load image on the boot loader.. 114 | // even if we don't have a filesystem 115 | EFI_HANDLE new_image_handle; 116 | int status = gBS->LoadImage( 117 | 0, // BootPolicy; ignored since addr is not NULL 118 | image_handle, 119 | boot_device, 120 | (void*) args->image_addr, 121 | args->image_size, 122 | &new_image_handle 123 | ); 124 | 125 | status = gBS->StartImage(new_image_handle, NULL, NULL); 126 | 127 | #ifdef CONFIG_EFILIB 128 | Print(u"status? %d\r\n", status); 129 | #else 130 | ST->ConOut->OutputString(ST->ConOut, status ? u"FAILED\r\n" : u"SUCCESS\r\n"); 131 | #endif 132 | 133 | // returning from here *should* resume UEFI 134 | return 0; 135 | } 136 | -------------------------------------------------------------------------------- /chainload/efi-entry.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern int efi_entry(void *, void*); 4 | 5 | void __attribute__((__naked__)) 6 | entry(void) 7 | { 8 | // restore the context from when UEFI invoked Linux 9 | // must match arch/x86/boot/compressed/head_64.S 10 | // and this must be invoked with the 0 page mapped 11 | // to us so that we can retrieve the context data 12 | void * UEFI_CONTEXT = (void*) 0x100; 13 | 14 | __asm__ __volatile__( 15 | "cli;" 16 | //"lldt 0x80(%0);" 17 | /* callee-saved registers */ 18 | "movq 0x00(%0), %%rbx;" 19 | "movq 0x08(%0), %%rbp;" 20 | "movq 0x10(%0), %%r12;" 21 | "movq 0x18(%0), %%r13;" 22 | "movq 0x20(%0), %%r14;" 23 | "movq 0x28(%0), %%r15;" 24 | /* control registers */ 25 | "movq 0x38(%0), %%rcx;" 26 | "movq %%rcx, %%cr0;" 27 | "movq 0x40(%0), %%rcx;" 28 | "movq %%rcx, %%cr3;" 29 | "movq 0x48(%0), %%rcx;" 30 | "movq %%rcx, %%cr4;" 31 | "movq 0x50(%0), %%rcx;" 32 | "movq %%rcx, %%cr8;" 33 | /* descriptor tables and segment registers*/ 34 | "lidt 0x60(%0);" 35 | "lgdt 0x70(%0);" 36 | "mov $0x30, %%rcx;" 37 | "mov %%cx, %%ds;" 38 | "mov %%cx, %%es;" 39 | "mov %%cx, %%fs;" 40 | "mov %%cx, %%gs;" 41 | "mov %%cx, %%ss;" 42 | 43 | /* long jump to set %cs; need to use temporary stack */ 44 | "lea 0x90(%0), %%rsp;" 45 | "pushq $0x38;" 46 | "lea new_universe(%%rip), %%rcx;" 47 | "pushq %%rcx;" 48 | "lretq;" 49 | // use uefi stack while calling efi_entry */ 50 | "new_universe: nop;" 51 | "movq 0x30(%0), %%rsp;" 52 | "movq 0x90(%0), %%rdi;" // image handle 53 | "movq 0x98(%0), %%rsi;" // system table pointer 54 | "lea efi_entry(%%rip), %%rcx;" 55 | "pushq %%rcx;" 56 | "retq;" 57 | : 58 | : "a"(UEFI_CONTEXT) 59 | : "memory" 60 | ); 61 | 62 | // should not be reached 63 | } 64 | -------------------------------------------------------------------------------- /chainload/loader.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Wrap the Linux kernel, initrd and commandline in an EFI PE. 3 | * 4 | * This is similar to the systemd EFI stub, except that it allocates 5 | * UEFI memory for the Linux kernel to run inside of, preserves the 6 | * UEFI context state and, by blocking the call to ExitBootServices, 7 | * allows Linux to return to UEFI. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "resume.h" 17 | #include "pe.h" 18 | 19 | 20 | static void 21 | context_save(uefi_context_t * const context) 22 | { 23 | __asm__ __volatile__( 24 | "mov %%cr3, %0;" 25 | "mov %%cr0, %1;" 26 | "mov %%cr4, %2;" 27 | "mov %%cr8, %3;" 28 | : "=r"(context->cr3), 29 | "=r"(context->cr0), 30 | "=r"(context->cr4), 31 | "=r"(context->cr8) 32 | : 33 | : "memory" 34 | ); 35 | 36 | __asm__ __volatile__( "sidt %0" : "=m"(context->idt) : : "memory"); 37 | __asm__ __volatile__( "sgdt %0" : "=m"(context->gdt) : : "memory"); 38 | __asm__ __volatile__( "sldt %0" : "=m"(context->ldt) : : "memory"); 39 | } 40 | 41 | static inline uint32_t virt2phys(const void * p) 42 | { 43 | return (uint32_t)(uintptr_t) p; 44 | } 45 | 46 | static void * alloc_lowmem(const unsigned size) 47 | { 48 | EFI_PHYSICAL_ADDRESS addr = UINT32_MAX; 49 | int err = gBS->AllocatePages( 50 | AllocateMaxAddress, 51 | EfiLoaderData, 52 | EFI_SIZE_TO_PAGES(size), 53 | &addr); 54 | if (EFI_ERROR(err)) 55 | return NULL; 56 | 57 | void * const ptr = (void*) addr; 58 | 59 | memset(ptr, 0, size); 60 | 61 | return ptr; 62 | } 63 | 64 | static void * alloc_phys(uintptr_t base, size_t len) 65 | { 66 | EFI_PHYSICAL_ADDRESS addr = base; 67 | 68 | int err = gBS->AllocatePages( 69 | AllocateAddress, 70 | EfiLoaderData, 71 | EFI_SIZE_TO_PAGES(len), 72 | &addr 73 | ); 74 | 75 | if (err) 76 | { 77 | Print(u"%016lx + %08x: memory allocation failed!\n", base, len); 78 | return NULL; 79 | } 80 | 81 | return (void*) base; 82 | } 83 | 84 | 85 | static int __attribute__((__noinline__)) 86 | exec_linux( 87 | EFI_HANDLE * image_handle, 88 | EFI_SYSTEM_TABLE * ST, 89 | struct boot_params * boot_params, 90 | uefi_context_t * const context 91 | ) 92 | { 93 | #ifdef __x86_64__ 94 | unsigned long start = boot_params->hdr.code32_start + 512; 95 | #else 96 | unsigned long start = boot_params->hdr.code32_start; 97 | #endif 98 | 99 | int (*linux_entry)(EFI_HANDLE *, EFI_SYSTEM_TABLE *, struct boot_params *) 100 | = (void*)(start + boot_params->hdr.handover_offset); 101 | 102 | context->image_handle = (uintptr_t) image_handle; 103 | context->system_table = (uintptr_t) ST; 104 | context->magic = UEFI_CONTEXT_MAGIC; 105 | 106 | __asm__ __volatile__( 107 | "cli;" 108 | "mov %%rsp, %0;" // cache the current stack 109 | "mov %%rbp, %1;" // cache the callee-saved registers 110 | "mov %%rbx, %2;" 111 | "mov %%r12, %3;" 112 | "mov %%r13, %4;" 113 | "mov %%r14, %5;" 114 | "mov %%r15, %6;" 115 | : "=m"(context->rsp), 116 | "=m"(context->rbp), 117 | "=m"(context->rbx), 118 | "=m"(context->r12), 119 | "=m"(context->r13), 120 | "=m"(context->r14), 121 | "=m"(context->r15) 122 | : 123 | : "memory", "cc" 124 | ); 125 | 126 | // this should be a jmpq *%rax 127 | return linux_entry(image_handle, ST, boot_params); 128 | } 129 | 130 | static void * orig_exit_boot_services; 131 | static EFI_STATUS EFIAPI 132 | do_not_exit_boot_services(EFI_HANDLE handle, UINTN mapkey) 133 | { 134 | (void) handle; 135 | (void) mapkey; 136 | 137 | Print(u"Not exiting boot services this time...\r\n"); 138 | gBS->ExitBootServices = orig_exit_boot_services; 139 | 140 | return EFI_SUCCESS; 141 | } 142 | 143 | 144 | EFI_STATUS 145 | //EFIAPI // not EFIAPI, since this is called via gnuefilib 146 | efi_main( 147 | EFI_HANDLE image_handle, 148 | EFI_SYSTEM_TABLE * const ST 149 | ) 150 | { 151 | InitializeLib(image_handle, ST); 152 | gBS = ST->BootServices; 153 | 154 | ST->ConOut->OutputString(ST->ConOut, u"UEFI string test\r\n"); 155 | Print(u"efilib says hello\r\n"); 156 | 157 | // get the memory address of our actual image 158 | EFI_LOADED_IMAGE * loaded_image; 159 | EFI_STATUS err = gBS->OpenProtocol( 160 | image_handle, 161 | &LoadedImageProtocol, 162 | (void **) &loaded_image, 163 | image_handle, 164 | NULL, 165 | EFI_OPEN_PROTOCOL_GET_PROTOCOL); 166 | if (err) 167 | { 168 | Print(u"Could not find own image. We're done here\n"); 169 | return EFI_NOT_FOUND; 170 | } 171 | 172 | void * const image = loaded_image->ImageBase; 173 | const size_t image_size = loaded_image->ImageSize; 174 | 175 | // find the kernel, initrd and command line PE sections 176 | size_t kernel_size = 0; 177 | size_t initrd_size = 0; 178 | size_t cmdline_size = 0; 179 | 180 | 181 | void * const kernel = pe_find_section(image, image_size, "linux", &kernel_size); 182 | void * const initrd = pe_find_section(image, image_size, "initrd", &initrd_size); 183 | void * const cmdline = pe_find_section(image, image_size, "cmdline", &cmdline_size); 184 | 185 | Print(u"SysTab: %016lx\n", (unsigned long) ST); 186 | Print(u"BootSvc: %016lx\n", (unsigned long) ST->BootServices); 187 | Print(u"kernel: %016lx + %08x\n", (unsigned long) kernel, kernel_size); 188 | Print(u"initrd: %016lx + %08x\n", (unsigned long) initrd, initrd_size); 189 | Print(u"cmdline: %016lx + %08x\n", (unsigned long) cmdline, cmdline_size); 190 | 191 | if (!kernel) 192 | { 193 | Print(u"NO KERNEL. WE'RE DONE HERE\n"); 194 | return EFI_NOT_FOUND; 195 | } 196 | 197 | // allocate some contiguous memory for the Linux application 198 | // start at 1GB so that it doesn't squash any other data 199 | // and so that the entire bottom entry for the CR3 is available 200 | EFI_PHYSICAL_ADDRESS linux_addr = 1 << 30; 201 | const size_t linux_size = 512 << 20; 202 | void * base = NULL; 203 | 204 | for(int i = 0 ; i < 64 ; i++) 205 | { 206 | base = alloc_phys(linux_addr, linux_size); 207 | if (base) 208 | break; 209 | 210 | //Print(u"AllocateAddress %016lx failed\r\n", (unsigned long) linux_addr); 211 | linux_addr += 1 << 30; 212 | } 213 | 214 | if (base == NULL) 215 | { 216 | Print(u"****** no memory allocated for linux! this is bad *****\r\n"); 217 | } else { 218 | Print(u"Reserved %016lx + %08x for Linux\n", linux_addr, linux_size); 219 | } 220 | 221 | // allocate some low memory for the boot params 222 | struct boot_params * const boot_params = alloc_lowmem(0x4000); 223 | struct boot_params * const image_params = kernel; 224 | 225 | Print(u"params: %016lx + %08x\n", (unsigned long) boot_params, cmdline_size); 226 | boot_params->hdr = image_params->hdr; 227 | boot_params->hdr.type_of_loader = 0xFF; 228 | 229 | const unsigned setup_sectors = image_params->hdr.setup_sects > 0 230 | ? image_params->hdr.setup_sects 231 | : 4; 232 | 233 | boot_params->hdr.code32_start = virt2phys(kernel) + (setup_sectors + 1) * 512; 234 | 235 | // reserve space in the command line for our memory allocation 236 | char * cmdline_copy = alloc_lowmem(cmdline_size + 256); 237 | boot_params->hdr.cmd_line_ptr = virt2phys(cmdline_copy); 238 | 239 | if (cmdline) 240 | { 241 | Print(u"cmdline: %016lx + %08x\n", (unsigned long) cmdline_copy, cmdline_size+1); 242 | memcpy(cmdline_copy, cmdline, cmdline_size); 243 | } 244 | 245 | // add in the memory allocation 246 | CHAR16 extra_cmd[256]; 247 | int extra_len = SPrint(extra_cmd, sizeof(extra_cmd), 248 | u" memmap=exactmap,128K@0G,512M@%luG", 249 | linux_addr >> 30 250 | ); 251 | for(int i = 0 ; i < extra_len ; i++) 252 | cmdline_copy[cmdline_size + i - 1] = (extra_cmd[i]) & 0xFF; 253 | 254 | boot_params->hdr.ramdisk_image = virt2phys(initrd); 255 | boot_params->hdr.ramdisk_size = initrd_size; 256 | 257 | 258 | //alloc_phys(0, 32768); // smp trampoline 259 | 260 | uefi_context_t * const context = UEFI_CONTEXT; 261 | //Print(u"magic offset=%x\n", ((uintptr_t) &context->image_handle) - ((uintptr_t) context)); 262 | 263 | context_save(context); 264 | Print(u"CR0=%016lx\n", context->cr0); 265 | Print(u"CR3=%016lx\n", context->cr3); 266 | Print(u"CR4=%016lx\n", context->cr4); 267 | Print(u"CR8=%016lx\n", context->cr8); 268 | const x86_descriptor_t * d = (void*) context->gdt; 269 | Print(u"GDT=%08lx + %04x\n", d->base, d->limit); 270 | d = (void*) context->idt; 271 | Print(u"IDT=%08lx + %04x\n", d->base, d->limit); 272 | d = (void*) context->ldt; 273 | Print(u"LDT=%08lx + %04x\n", d->base, d->limit); 274 | 275 | orig_exit_boot_services = gBS->ExitBootServices; 276 | gBS->ExitBootServices = do_not_exit_boot_services; 277 | err = exec_linux(image_handle, ST, boot_params, context); 278 | 279 | // restore exit boot services, in case they didn't call it 280 | gBS->ExitBootServices = orig_exit_boot_services; 281 | 282 | Print(u"Welcome back! Hope you had fun in Linux: %d\n", err); 283 | 284 | // returning from here *should* resume UEFI 285 | return 0; 286 | } 287 | -------------------------------------------------------------------------------- /chainload/pe.c: -------------------------------------------------------------------------------- 1 | /* 2 | * xen/common/efi/pe.c 3 | * 4 | * PE executable header parser. 5 | * 6 | * Derived from https://github.com/systemd/systemd/blob/master/src/boot/efi/pe.c 7 | * commit 07d5ed536ec0a76b08229c7a80b910cb9acaf6b1 8 | * 9 | * Copyright (C) 2015 Kay Sievers 10 | * Copyright (C) 2020 Trammell Hudson 11 | * 12 | * This program is free software; you can redistribute it and/or modify it 13 | * under the terms of the GNU Lesser General Public License as published by 14 | * the Free Software Foundation; either version 2.1 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, but 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 | * Lesser General Public License for more details. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "pe.h" 29 | 30 | struct DosFileHeader { 31 | UINT8 Magic[2]; 32 | UINT16 LastSize; 33 | UINT16 nBlocks; 34 | UINT16 nReloc; 35 | UINT16 HdrSize; 36 | UINT16 MinAlloc; 37 | UINT16 MaxAlloc; 38 | UINT16 ss; 39 | UINT16 sp; 40 | UINT16 Checksum; 41 | UINT16 ip; 42 | UINT16 cs; 43 | UINT16 RelocPos; 44 | UINT16 nOverlay; 45 | UINT16 reserved[4]; 46 | UINT16 OEMId; 47 | UINT16 OEMInfo; 48 | UINT16 reserved2[10]; 49 | UINT32 ExeHeader; 50 | }; 51 | 52 | #if defined(__arm__) || defined (__aarch64__) 53 | #define PE_HEADER_MACHINE 0xaa64 54 | #elif defined(__x86_64__) 55 | #define PE_HEADER_MACHINE 0x8664 56 | #else 57 | #error "Unknown architecture" 58 | #endif 59 | 60 | struct PeFileHeader { 61 | UINT16 Machine; 62 | UINT16 NumberOfSections; 63 | UINT32 TimeDateStamp; 64 | UINT32 PointerToSymbolTable; 65 | UINT32 NumberOfSymbols; 66 | UINT16 SizeOfOptionalHeader; 67 | UINT16 Characteristics; 68 | }; 69 | 70 | struct PeHeader { 71 | UINT8 Magic[4]; 72 | struct PeFileHeader FileHeader; 73 | }; 74 | 75 | struct PeSectionHeader { 76 | CHAR8 Name[8]; 77 | UINT32 VirtualSize; 78 | UINT32 VirtualAddress; 79 | UINT32 SizeOfRawData; 80 | UINT32 PointerToRawData; 81 | UINT32 PointerToRelocations; 82 | UINT32 PointerToLinenumbers; 83 | UINT16 NumberOfRelocations; 84 | UINT16 NumberOfLinenumbers; 85 | UINT32 Characteristics; 86 | }; 87 | 88 | static int pe_name_compare(const struct PeSectionHeader *sect, 89 | const char *name) 90 | { 91 | size_t i; 92 | 93 | if ( sect->Name[0] != '.' ) 94 | return 0; 95 | 96 | for ( i = 1; i < sizeof(sect->Name); i++ ) 97 | { 98 | const char c = sect->Name[i]; 99 | 100 | if ( c != name[i - 1] ) 101 | return 0; 102 | if ( c == '\0' ) 103 | return 1; 104 | } 105 | 106 | return name[i - 1] == '\0'; 107 | } 108 | 109 | void *pe_find_section(void *image, const size_t image_size, 110 | const char *section_name, size_t *size_out) 111 | { 112 | const struct DosFileHeader *dos = image; 113 | const struct PeHeader *pe; 114 | const struct PeSectionHeader *sect; 115 | size_t offset, i; 116 | 117 | if ( image_size < sizeof(*dos) || 118 | memcmp(dos->Magic, "MZ", 2) != 0 ) 119 | return NULL; 120 | 121 | offset = dos->ExeHeader; 122 | pe = image + offset; 123 | 124 | offset += sizeof(*pe); 125 | if ( image_size < offset || 126 | memcmp(pe->Magic, "PE\0\0", 4) != 0 ) 127 | return NULL; 128 | 129 | if ( pe->FileHeader.Machine != PE_HEADER_MACHINE ) 130 | return NULL; 131 | 132 | offset += pe->FileHeader.SizeOfOptionalHeader; 133 | 134 | for ( i = 0; i < pe->FileHeader.NumberOfSections; i++ ) 135 | { 136 | sect = image + offset; 137 | if ( image_size < offset + sizeof(*sect) ) 138 | return NULL; 139 | 140 | if ( !pe_name_compare(sect, section_name) ) 141 | { 142 | offset += sizeof(*sect); 143 | continue; 144 | } 145 | 146 | if ( image_size < sect->VirtualSize + sect->VirtualAddress ) 147 | { 148 | Print(u"PE invalid section size %08x + address %016lx > size %016lx\n", sect->VirtualSize, sect->VirtualAddress, image_size); 149 | return NULL; 150 | } 151 | 152 | if ( size_out ) 153 | *size_out = sect->VirtualSize; 154 | 155 | return image + sect->VirtualAddress; 156 | } 157 | 158 | return NULL; 159 | } 160 | -------------------------------------------------------------------------------- /chainload/pe.h: -------------------------------------------------------------------------------- 1 | #ifndef _loader_pe_h_ 2 | #define _loader_pe_h_ 3 | 4 | #include 5 | 6 | extern void * 7 | pe_find_section( 8 | void *image, 9 | const size_t image_size, 10 | const char *section_name, 11 | size_t *size_out 12 | ); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /chainload/resume.h: -------------------------------------------------------------------------------- 1 | #ifndef _uefi_resume_h_ 2 | #define _uefi_resume_h_ 3 | 4 | /** 5 | * UEFI Context resuming code. 6 | * 7 | * This stores the CPU state that needs to be restored 8 | * when kexec from Linux back to the UEFI firmware. 9 | */ 10 | 11 | #include 12 | 13 | typedef struct 14 | __attribute__((__packed__)) 15 | { 16 | uint16_t limit; 17 | uint64_t base; 18 | } x86_descriptor_t; 19 | 20 | typedef struct 21 | { 22 | uint64_t rbx; 23 | uint64_t rbp; 24 | uint64_t r12; 25 | uint64_t r13; 26 | uint64_t r14; 27 | uint64_t r15; 28 | uint64_t rsp; 29 | uint64_t cr0; 30 | uint64_t cr3; 31 | uint64_t cr4; 32 | uint64_t cr8; 33 | uint64_t resv0; 34 | //x86_descriptor_t idt; 35 | uint8_t idt[16]; 36 | //x86_descriptor_t gdt; 37 | uint8_t gdt[16]; 38 | //x86_descriptor_t ldt; 39 | uint8_t ldt[16]; 40 | uint64_t image_handle; 41 | uint64_t system_table; 42 | uint64_t magic; 43 | uint64_t chainload_ptr; 44 | uint64_t temp_stack[4]; 45 | } uefi_context_t; 46 | 47 | #define UEFI_CONTEXT_OFFSET ((uint64_t) 0x100) 48 | #define UEFI_CONTEXT_MAGIC ((uint64_t) 0xdecafbad) 49 | #define UEFI_CONTEXT ((uefi_context_t*) UEFI_CONTEXT_OFFSET) 50 | 51 | #endif 52 | 53 | -------------------------------------------------------------------------------- /chainload/unify-kernel: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Derived from safeboot unify-kernel 3 | # Creates a unified kernel image with the named sections and files 4 | # (typically `kernel`, `initrd`, `cmdline`, and `osrel`) bundled into 5 | # an EFI executable. 6 | # 7 | # ' 8 | 9 | usage=' 10 | Usage: 11 | unify-kernel bootx64.efi \ 12 | linux=bzImage \ 13 | initrd=initrd.cpio.xz \ 14 | cmdline=cmdline.txt \ 15 | ... 16 | 17 | To use a different stub than loader.efi, pass in a 18 | stub=/path/to/loader.efi argument. 19 | ' 20 | 21 | die() { echo >&2 "$@" ; exit 1 ; } 22 | warn() { echo >&2 "$@" ; } 23 | 24 | unified="$1" 25 | shift 26 | 27 | if [ -z "$unified" ]; then 28 | die "$usage" 29 | fi 30 | 31 | # "$PREFIX/usr/lib/systemd/boot/efi/linuxx64.efi.stub" \ 32 | STUB="$(dirname $0)/loader.efi" 33 | if [ ! -r "$STUB" ]; then 34 | die "Unable to find EFI stub $stub" 35 | fi 36 | 37 | 38 | sections=() 39 | offset=$((0x20000)) 40 | blocksize=$((0x10000)) 41 | 42 | # Build the list of sections to add, splitting on = signs 43 | for section in "$@"; do 44 | name="${section%=*}" 45 | file="${section#*=}" 46 | if [ "$name" = "$section" ]; then 47 | die "$name: format error (not name=file?)" 48 | fi 49 | if [ ! -r "$file" ]; then 50 | die "$file: unable to read for section $name" 51 | fi 52 | 53 | if [ "$name" = "stub" ]; then 54 | STUB="$name" 55 | continue 56 | fi 57 | 58 | size="$(wc -c < "$file")" 59 | warn ".$name=$file: $size @ $offset" 60 | 61 | sections+=( \ 62 | --add-section ".$name=$file" \ 63 | --change-section-vma ".$name=$offset" \ 64 | ) 65 | 66 | # round up the offset to the next block size 67 | offset="$(( (offset + size + blocksize-1) / blocksize * blocksize ))" 68 | done 69 | 70 | 71 | 72 | objcopy \ 73 | "${sections[@]}" \ 74 | "$STUB" \ 75 | "$unified" \ 76 | || die "$unified: unable to create" 77 | 78 | sha256sum "$unified" 79 | -------------------------------------------------------------------------------- /config/OVMF_VARS.fd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osresearch/safeboot-loader/4c5ed9155ed0c2408a1040931d8bb4b70b3c278c/config/OVMF_VARS.fd -------------------------------------------------------------------------------- /config/RamDiskDxe.efi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osresearch/safeboot-loader/4c5ed9155ed0c2408a1040931d8bb4b70b3c278c/config/RamDiskDxe.efi -------------------------------------------------------------------------------- /config/cmdline-5.4.117.txt: -------------------------------------------------------------------------------- 1 | earlyprintk=serial,ttyS0,115200 console=tty0 console=ttyS0,115200 noefi acpi=off 2 | -------------------------------------------------------------------------------- /config/linux-5.4.117.config: -------------------------------------------------------------------------------- 1 | # CONFIG_LOCALVERSION_AUTO is not set 2 | CONFIG_KERNEL_XZ=y 3 | CONFIG_DEFAULT_HOSTNAME="safeboot" 4 | # CONFIG_CROSS_MEMORY_ATTACH is not set 5 | CONFIG_HIGH_RES_TIMERS=y 6 | CONFIG_TASKSTATS=y 7 | CONFIG_TASK_DELAY_ACCT=y 8 | CONFIG_BLK_DEV_INITRD=y 9 | CONFIG_INITRAMFS_SOURCE="../../kernel/dev.cpio" 10 | # CONFIG_RD_GZIP is not set 11 | # CONFIG_RD_LZMA is not set 12 | # CONFIG_RD_LZO is not set 13 | # CONFIG_RD_LZ4 is not set 14 | CONFIG_INITRAMFS_COMPRESSION_XZ=y 15 | # CONFIG_SGETMASK_SYSCALL is not set 16 | # CONFIG_SYSFS_SYSCALL is not set 17 | # CONFIG_FHANDLE is not set 18 | # CONFIG_BUG is not set 19 | # CONFIG_PCSPKR_PLATFORM is not set 20 | # CONFIG_BASE_FULL is not set 21 | # CONFIG_TIMERFD is not set 22 | # CONFIG_AIO is not set 23 | # CONFIG_IO_URING is not set 24 | # CONFIG_MEMBARRIER is not set 25 | # CONFIG_KALLSYMS is not set 26 | # CONFIG_RSEQ is not set 27 | CONFIG_EMBEDDED=y 28 | # CONFIG_VM_EVENT_COUNTERS is not set 29 | # CONFIG_COMPAT_BRK is not set 30 | CONFIG_SLOB=y 31 | # CONFIG_SLAB_MERGE_DEFAULT is not set 32 | # CONFIG_ZONE_DMA is not set 33 | # CONFIG_X86_MPPARSE is not set 34 | # CONFIG_X86_EXTENDED_PLATFORM is not set 35 | # CONFIG_SCHED_OMIT_FRAME_POINTER is not set 36 | CONFIG_PROCESSOR_SELECT=y 37 | # CONFIG_DMI is not set 38 | # CONFIG_X86_MCE is not set 39 | # CONFIG_X86_VSYSCALL_EMULATION is not set 40 | # CONFIG_MICROCODE is not set 41 | CONFIG_X86_MSR=y 42 | CONFIG_EFI=y 43 | CONFIG_EFI_STUB=y 44 | # CONFIG_SECCOMP is not set 45 | CONFIG_HZ_100=y 46 | CONFIG_KEXEC=y 47 | CONFIG_CMDLINE_BOOL=y 48 | # CONFIG_MODIFY_LDT_SYSCALL is not set 49 | # CONFIG_SUSPEND is not set 50 | # CONFIG_X86_PM_TIMER is not set 51 | # CONFIG_ISA_DMA_API is not set 52 | CONFIG_EFI_VARS=y 53 | CONFIG_EFI_BOOTLOADER_CONTROL=y 54 | CONFIG_EFI_CAPSULE_LOADER=y 55 | CONFIG_RESET_ATTACK_MITIGATION=y 56 | # CONFIG_VIRTUALIZATION is not set 57 | CONFIG_JUMP_LABEL=y 58 | CONFIG_MODULES=y 59 | CONFIG_MODULE_UNLOAD=y 60 | # CONFIG_MODULE_SIG_ALL is not set 61 | # CONFIG_COREDUMP is not set 62 | CONFIG_TRANSPARENT_HUGEPAGE=y 63 | CONFIG_NET=y 64 | CONFIG_PACKET=y 65 | CONFIG_UNIX=y 66 | CONFIG_INET=y 67 | CONFIG_IP_MULTICAST=y 68 | CONFIG_SYN_COOKIES=y 69 | # CONFIG_INET_DIAG is not set 70 | # CONFIG_IPV6 is not set 71 | CONFIG_VLAN_8021Q=y 72 | # CONFIG_WIRELESS is not set 73 | CONFIG_UEFIDEV=y 74 | CONFIG_UEFINET=y 75 | CONFIG_UEFIBLOCK=y 76 | CONFIG_UEFITPM=y 77 | CONFIG_DEVTMPFS=y 78 | # CONFIG_STANDALONE is not set 79 | # CONFIG_PREVENT_FIRMWARE_BUILD is not set 80 | # CONFIG_FW_LOADER is not set 81 | # CONFIG_ALLOW_DEV_COREDUMP is not set 82 | # CONFIG_PNP_DEBUG_MESSAGES is not set 83 | CONFIG_BLK_DEV_LOOP=y 84 | CONFIG_MD=y 85 | CONFIG_BLK_DEV_DM=y 86 | CONFIG_DM_CRYPT=y 87 | CONFIG_DM_SNAPSHOT=y 88 | CONFIG_DM_VERITY=y 89 | CONFIG_NETDEVICES=y 90 | # CONFIG_ETHERNET is not set 91 | # CONFIG_WLAN is not set 92 | # CONFIG_INPUT_MOUSE is not set 93 | CONFIG_SERIAL_8250=y 94 | # CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set 95 | CONFIG_SERIAL_8250_CONSOLE=y 96 | CONFIG_TTY_PRINTK=y 97 | # CONFIG_HW_RANDOM is not set 98 | CONFIG_NVRAM=y 99 | CONFIG_TCG_TPM=y 100 | CONFIG_TCG_TIS=y 101 | CONFIG_I2C=y 102 | CONFIG_PTP_1588_CLOCK=y 103 | # CONFIG_HWMON is not set 104 | CONFIG_THERMAL_WRITABLE_TRIPS=y 105 | CONFIG_THERMAL_GOV_USER_SPACE=y 106 | CONFIG_FB=y 107 | CONFIG_FB_EFI=y 108 | CONFIG_FRAMEBUFFER_CONSOLE=y 109 | CONFIG_LOGO=y 110 | # CONFIG_HID is not set 111 | # CONFIG_USB_SUPPORT is not set 112 | CONFIG_SYNC_FILE=y 113 | # CONFIG_VIRTIO_MENU is not set 114 | # CONFIG_X86_PLATFORM_DEVICES is not set 115 | # CONFIG_IOMMU_SUPPORT is not set 116 | CONFIG_EXT4_FS=y 117 | # CONFIG_DNOTIFY is not set 118 | # CONFIG_INOTIFY_USER is not set 119 | CONFIG_ISO9660_FS=y 120 | CONFIG_JOLIET=y 121 | CONFIG_UDF_FS=y 122 | CONFIG_MSDOS_FS=y 123 | CONFIG_VFAT_FS=y 124 | CONFIG_NTFS_FS=y 125 | CONFIG_TMPFS=y 126 | CONFIG_HUGETLBFS=y 127 | CONFIG_EFIVAR_FS=y 128 | # CONFIG_MISC_FILESYSTEMS is not set 129 | CONFIG_NFS_FS=y 130 | CONFIG_NLS_CODEPAGE_437=y 131 | CONFIG_NLS_ASCII=y 132 | CONFIG_NLS_ISO8859_1=y 133 | CONFIG_NLS_UTF8=y 134 | CONFIG_SECURITY=y 135 | CONFIG_FORTIFY_SOURCE=y 136 | CONFIG_SECURITY_LOCKDOWN_LSM=y 137 | CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y 138 | CONFIG_CRYPTO_XTS=y 139 | CONFIG_CRYPTO_HMAC=y 140 | CONFIG_CRYPTO_MD5=y 141 | CONFIG_CRYPTO_SHA1_SSSE3=y 142 | CONFIG_CRYPTO_SHA256_SSSE3=y 143 | CONFIG_CRYPTO_AES=y 144 | CONFIG_CRYPTO_AES_NI_INTEL=y 145 | CONFIG_IRQ_POLL=y 146 | CONFIG_PRINTK_TIME=y 147 | CONFIG_FRAME_WARN=1024 148 | # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set 149 | # CONFIG_DEBUG_MISC is not set 150 | # CONFIG_FTRACE is not set 151 | # CONFIG_RUNTIME_TESTING_MENU is not set 152 | # CONFIG_STRICT_DEVMEM is not set 153 | # CONFIG_X86_VERBOSE_BOOTUP is not set 154 | # CONFIG_X86_DEBUG_FPU is not set 155 | CONFIG_UNWINDER_GUESS=y 156 | -------------------------------------------------------------------------------- /config/linux-5.4.117.patch: -------------------------------------------------------------------------------- 1 | diff -u --recursive ../build/clean/linux-5.4.117/drivers/Kconfig linux-5.4.117/drivers/Kconfig 2 | --- ../build/clean/linux-5.4.117/drivers/Kconfig 2021-05-07 08:51:38.000000000 +0000 3 | +++ linux-5.4.117/drivers/Kconfig 2022-02-16 14:03:39.241112549 +0000 4 | @@ -9,6 +9,7 @@ 5 | source "drivers/pcmcia/Kconfig" 6 | source "drivers/rapidio/Kconfig" 7 | 8 | +source "drivers/uefidev/Kconfig" 9 | 10 | source "drivers/base/Kconfig" 11 | 12 | diff -u --recursive ../build/clean/linux-5.4.117/drivers/Makefile linux-5.4.117/drivers/Makefile 13 | --- ../build/clean/linux-5.4.117/drivers/Makefile 2021-05-07 08:51:38.000000000 +0000 14 | +++ linux-5.4.117/drivers/Makefile 2022-02-16 14:05:22.684515177 +0000 15 | @@ -186,3 +186,5 @@ 16 | obj-$(CONFIG_GNSS) += gnss/ 17 | obj-$(CONFIG_INTERCONNECT) += interconnect/ 18 | obj-$(CONFIG_COUNTER) += counter/ 19 | + 20 | +obj-$(CONFIG_UEFIDEV) += uefidev/ 21 | -------------------------------------------------------------------------------- /images/chainload.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osresearch/safeboot-loader/4c5ed9155ed0c2408a1040931d8bb4b70b3c278c/images/chainload.jpg -------------------------------------------------------------------------------- /initrd/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Build a safeboot initrd.cpio 3 | # 4 | O ?= ../build 5 | WORKDIR := $O/initrd 6 | 7 | all: $O/initrd.cpio.xz 8 | 9 | $O: 10 | mkdir -p $O 11 | 12 | $(WORKDIR)/gitstatus: files.txt 13 | rm -rf "$(dir $@)" 14 | mkdir -p "$(dir $@)" 15 | ./populate "$(dir $@)" "$<" 16 | git status -s > "$@" 17 | 18 | -include $O/initrd.deps 19 | 20 | $O/initrd.cpio: $(WORKDIR)/gitstatus 21 | ( cd $(dir $<) ; \ 22 | find . -print0 \ 23 | | cpio \ 24 | -0 \ 25 | -o \ 26 | -H newc \ 27 | ) \ 28 | | ./cpio-clean \ 29 | ../kernel/dev.cpio \ 30 | - \ 31 | > $@ 32 | sha256sum $@ 33 | 34 | $O/initrd.cpio.xz: $O/initrd.cpio 35 | xz \ 36 | --check=crc32 \ 37 | --lzma2=dict=256KiB \ 38 | --threads=0 \ 39 | < "$<" \ 40 | | dd bs=512 conv=sync status=none \ 41 | > "$@.tmp" 42 | @if ! cmp --quiet "$@.tmp" "$@" ; then \ 43 | mv "$@.tmp" "$@" ; \ 44 | else \ 45 | echo "$@: unchanged" ; \ 46 | rm "$@.tmp" ; \ 47 | fi 48 | sha256sum $@ 49 | 50 | $O/initrd.cpio.bz: $O/initrd.cpio 51 | bzip2 -z \ 52 | < "$<" \ 53 | | dd bs=512 conv=sync status=none \ 54 | > "$@.tmp" 55 | @if ! cmp --quiet "$@.tmp" "$@" ; then \ 56 | mv "$@.tmp" "$@" ; \ 57 | else \ 58 | echo "$@: unchanged" ; \ 59 | rm "$@.tmp" ; \ 60 | fi 61 | sha256sum $@ 62 | 63 | -------------------------------------------------------------------------------- /initrd/attest-boot: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | # This is a sample remote attestation that retrieves a bitlocker key 3 | # create a ramdisk with it, and then chainloads into the Microsoft 4 | # bootloader 5 | 6 | boot_dev="uefi6" 7 | url="http://10.0.2.2:5000/" 8 | image="/boot/EFI/Boot/bootx64.efi" 9 | assets="/tmp/assets.tgz" 10 | 11 | if [ -n "$1" ]; then 12 | url="$1" 13 | fi 14 | if [ -n "$2" ]; then 15 | boot_dev="$2" 16 | fi 17 | if [ -n "$3" ]; then 18 | image="$3" 19 | fi 20 | 21 | die() { echo >&2 "$*" ; rm -f "$assets" ; exit 1 ; } 22 | 23 | ifconfig eth0 10.0.2.15 24 | 25 | safeboot-attest "$url" > "$assets" \ 26 | || die "$url: attestation failed!" 27 | 28 | ramdisk-create 1024 /ramdisk \ 29 | || die "ramdisk failed" 30 | 31 | tar \ 32 | -C /ramdisk \ 33 | --no-same-owner \ 34 | -xvf \ 35 | "$assets" \ 36 | || die "untar of assets failed" 37 | 38 | rm -f "$assets" 39 | 40 | ls -Fla /ramdisk 41 | 42 | umount /ramdisk 43 | 44 | mount -o ro "/dev/$boot_dev" /boot \ 45 | || die "/dev/$boot_dev did not mount" 46 | 47 | 48 | # bug - the nic has to be shutdown before starting windows 49 | ifconfig eth0 down 50 | 51 | chainload -v \ 52 | -d "$boot_dev" \ 53 | "$image" \ 54 | || die "chainload failed" 55 | 56 | -------------------------------------------------------------------------------- /initrd/cpio-clean: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # Clean all non-deterministric fields in a newc cpio file 3 | # 4 | # Items fixed: 5 | # Files are sorted by name 6 | # Inode numbers are based on the hash of the filename 7 | # File timestamp is set to 1970-01-01T00:00:00 8 | # uid/gid are set to root 9 | # check field is zeroed 10 | # nlinks is set to zero, since the filesystem manages it 11 | # 12 | use warnings; 13 | use strict; 14 | use Data::Dumper; 15 | use Digest::MD5 'md5_hex'; 16 | 17 | # struct cpio_newc_header { 18 | # char c_magic[6]; -6 19 | # char c_ino[8]; -- set to a monotonic value 0 20 | # char c_mode[8]; 8 21 | # char c_uid[8]; 16 22 | # char c_gid[8]; 24 23 | # char c_nlink[8]; 32 24 | # char c_mtime[8]; 40 -- set to zero 25 | # char c_filesize[8]; 48 26 | # char c_devmajor[8]; 56 27 | # char c_devminor[8]; 64 28 | # char c_rdevmajor[8]; 72 29 | # char c_rdevminor[8]; 80 30 | # char c_namesize[8]; 88 31 | # char c_check[8]; 96 32 | # }; // 104 33 | # followed by namesize bytes of name (padded to be a multiple of 4) 34 | # followed dby filesize bytes of file (padded to be a multiple of 4) 35 | 36 | # Read the entire file at once 37 | undef $/; 38 | 39 | # Generate a map of all of the files in the cpio archive 40 | # This will also merge multiple cpio files 41 | my %entries; 42 | my $trailer; 43 | 44 | while(<>) 45 | { 46 | for(my $i = 0 ; $i < length $_ ; ) 47 | { 48 | my $magic = substr($_, $i, 6); 49 | if ($magic ne "070701") 50 | { 51 | die "$ARGV: offset $i: invalid magic '$magic'\n"; 52 | } 53 | 54 | my $namesize = substr($_, $i + 6+88, 8); 55 | my $filesize = substr($_, $i + 6+48, 8); 56 | 57 | if ($namesize =~ /[^0-9A-Fa-f]/) 58 | { 59 | die "$ARGV: offset $i: invalid characters in namesize '$namesize'\n"; 60 | } 61 | 62 | if ($filesize =~ /[^0-9A-Fa-f]/) 63 | { 64 | die "$ARGV: offset $i: invalid characters in filesize '$filesize'\n"; 65 | } 66 | 67 | # Convert them to hex 68 | $namesize = hex $namesize; 69 | $filesize = hex $filesize; 70 | 71 | #print STDERR "name: '$namesize', filesize: '$filesize'\n"; 72 | 73 | my $name = substr($_, $i + 6+104, $namesize); 74 | #print STDERR Dumper($name); 75 | 76 | # Align the header size to be a multiple of four bytes 77 | my $entry_size = (6+104 + $namesize + 3) & ~3; 78 | $entry_size += ($filesize + 3) & ~3; 79 | 80 | my $entry = substr($_, $i, $entry_size); 81 | $i += $entry_size; 82 | 83 | if ($name =~ /^TRAILER!!!/) 84 | { 85 | $trailer = $entry; 86 | last; 87 | } 88 | 89 | $entries{$name} = $entry; 90 | } 91 | 92 | die "$ARGV: No trailer!\n" unless $trailer; 93 | } 94 | 95 | # Apply the cleaning to each one 96 | for my $filename (sort keys %entries) 97 | { 98 | my $entry = $entries{$filename}; 99 | my $zero = sprintf "%08x", 0; 100 | 101 | # inodes are hashed to be deterministic 102 | # and hopefully not colliding 103 | my $md5 = md5_hex($filename); 104 | my $d0 = hex substr($md5, 0, 8) ; 105 | my $d1 = hex substr($md5, 8, 8) ; 106 | my $d2 = hex substr($md5, 16, 8) ; 107 | my $d3 = hex substr($md5, 24, 8) ; 108 | my $hash = sprintf "%08x", $d0 ^ $d1 ^ $d2 ^ $d3; 109 | 110 | #warn "$filename: $md5 -> $hash\n"; 111 | substr($entry, 6 + 0, 8) = $hash; 112 | 113 | # set timestamps to zero 114 | substr($entry, 6 + 40, 8) = $zero; 115 | 116 | # remove group/user permissions, leaving only 117 | # the owner bits intact. 118 | my $mode = hex substr($entry, 6 + 8, 8); 119 | $mode &= ~0077; 120 | #$mode |= $mode >> 3 | $mode >> 6; 121 | substr($entry, 6 + 8, 8) = sprintf "%08X", $mode; 122 | 123 | # set uid/gid to zero 124 | substr($entry, 6 + 16, 8) = $zero; 125 | substr($entry, 6 + 24, 8) = $zero; 126 | 127 | # zero out the nlinks, since it is managed by the real fs 128 | substr($entry, 6 + 32, 8) = $zero; 129 | 130 | # set the device major/minor to zero 131 | substr($entry, 6 + 56, 8) = $zero; 132 | substr($entry, 6 + 64, 8) = $zero; 133 | 134 | # set check to zero 135 | substr($entry, 6 + 96, 8) = $zero; 136 | 137 | $entries{$filename} = $entry; 138 | } 139 | 140 | 141 | # Output them in sorted order 142 | my $out = join '', map { $entries{$_} } sort keys %entries; 143 | #for my $filename (sort keys %entries) 144 | #{ 145 | #$out .= $entries{$filename}; 146 | #} 147 | 148 | # Output the trailer to mark the end of the archive 149 | $out .= $trailer; 150 | 151 | # Pad to 512-bytes for kernel initrd reasons 152 | my $unaligned = length($out) % 512; 153 | $out .= chr(0x00) x (512 - $unaligned) 154 | if $unaligned != 0; 155 | 156 | print $out; 157 | __END__ 158 | -------------------------------------------------------------------------------- /initrd/files.txt: -------------------------------------------------------------------------------- 1 | # setup a few directories 2 | mkdir bin 3 | mkdir lib 4 | mkdir lib64 5 | symlink ../lib64 lib/x86_64-linux-gnu 6 | 7 | ./init . 8 | 9 | # tool to build a UEFI ramdisk image 10 | # and perform an attested boot 11 | ./ramdisk-create 12 | ./attest-boot 13 | 14 | # core utils 15 | /bin/bash 16 | symlink bash bin/sh 17 | /usr/bin/mount 18 | /usr/bin/umount 19 | /usr/bin/cp 20 | /usr/bin/mv 21 | /usr/bin/cmp 22 | /usr/bin/ls 23 | /usr/bin/cat 24 | /usr/bin/echo 25 | /usr/bin/head 26 | /usr/bin/tail 27 | /usr/bin/mkdir 28 | /usr/bin/setsid 29 | /usr/bin/dd 30 | /usr/bin/dirname 31 | /usr/bin/grep 32 | /usr/bin/xxd 33 | /usr/bin/mktemp 34 | /usr/bin/date 35 | /usr/bin/printf 36 | /usr/bin/rm 37 | /usr/bin/sleep 38 | /usr/bin/tar 39 | /usr/bin/cpio 40 | /usr/bin/gzip 41 | 42 | # disk management 43 | /sbin/fdisk 44 | /sbin/mkfs.ext4 45 | /sbin/mkfs.vfat 46 | /sbin/fsck.vfat 47 | /usr/sbin/losetup 48 | 49 | # networking stuff 50 | /usr/bin/openssl 51 | /usr/bin/curl 52 | /usr/bin/ping 53 | /usr/bin/nc 54 | /usr/sbin/ifconfig 55 | /usr/sbin/route 56 | #/usr/bin/efibootmgr 57 | 58 | # cryptdisk setup 59 | /sbin/cryptsetup 60 | /sbin/dmsetup 61 | /sbin/lvm 62 | 63 | # chainload to kexec stuff 64 | ../build/chainload/chainload 65 | ../chainload/boot.sh 66 | ../config/RamDiskDxe.efi 67 | 68 | # hack to bring in tpm2 and its dlopen'ed library 69 | /usr/local/bin/tpm2 70 | /usr/local/lib/libtss2-tcti-device.so.0 lib 71 | 72 | #../../safeboot-attest/client/safeboot-attest 73 | 74 | # inventory collection 75 | /usr/sbin/dmidecode 76 | /usr/bin/lshw 77 | -------------------------------------------------------------------------------- /initrd/init: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mkdir -p /proc /sys /tmp /dev /etc /root /run /boot 3 | mount -t proc none /proc 4 | mount -t devtmpfs none /dev 5 | mount -t sysfs none /sys 6 | mount -t efivarfs none /sys/firmware/efi/efivars 7 | mount -t securityfs none /sys/kernel/security 8 | 9 | echo "Hello, initrd" > /dev/console 10 | echo "Hello, initrd (ttyprintk)" > /dev/ttyprintk 11 | exec < /dev/console >/dev/console 2>/dev/console 12 | 13 | export TPM2TOOLS_TCTI="device:/dev/tpm0" 14 | 15 | export PS1='\w# ' 16 | if [ -x /bin/setsid ]; then 17 | exec /bin/setsid -c /bin/bash 18 | fi 19 | 20 | # fall back to a normal shell with no job control 21 | exec /bin/bash 22 | -------------------------------------------------------------------------------- /initrd/populate: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # Populate an initrd directory with the files on the command line 3 | use warnings; 4 | use strict; 5 | use File::Basename 'basename'; 6 | use File::Copy 'cp'; 7 | 8 | my $top = shift || './'; 9 | my $lib_dir = "$top/lib64"; 10 | my $bin_dir = "$top/bin"; 11 | 12 | my %libs; 13 | 14 | my $target = $top; 15 | $target =~ s:/$::; 16 | 17 | open DEPS, ">", "$target.deps" 18 | or die "$target.deps: Unable to create dependency file: $!\n"; 19 | 20 | print DEPS "# Autogenerated by $0\n"; 21 | print DEPS "$target/gitstatus: \\\n"; 22 | 23 | # for libraries 24 | my $UNAME = `uname -r`; 25 | chomp($UNAME); 26 | 27 | while(<>) 28 | { 29 | chomp; 30 | 31 | # strip comments and skip blank lines 32 | s/\s*#.*$//; 33 | next if /^$/; 34 | 35 | 36 | my ($bin,$dest,$target) = split /\s+/, $_; 37 | 38 | if ($bin eq 'symlink') 39 | { 40 | system 'ln', '-sf', "$dest", "$top/$target" 41 | and die "ln $dest -> $top/$target: $!\n"; 42 | next; 43 | } 44 | 45 | if ($bin eq 'mkdir') 46 | { 47 | system 'mkdir', '-p', "$top/$dest" 48 | and die "mkdir $top/$dest: $!\n"; 49 | next; 50 | } 51 | 52 | $dest ||= "/bin"; 53 | print DEPS "\t$bin \\\n"; 54 | 55 | system 'mkdir', '-p', "$top/$dest" 56 | and die "mkdir $top/$dest: $!\n"; 57 | 58 | $bin =~ s/\$UNAME/$UNAME/g; 59 | 60 | cp($bin, "$top/$dest/" . basename($bin)) 61 | or die "cp $bin $top/$dest/: Unable to copy: $!\n"; 62 | 63 | my @deps = `ldd "$bin" 2>&1` 64 | or next; 65 | 66 | for (@deps) 67 | { 68 | chomp; 69 | next if m/not a dynamic executable/; 70 | 71 | my ($short,$lib,$addr) = /^ 72 | \s+ 73 | (.*?\s+=>\s+)? 74 | (.*?) 75 | \s+ 76 | \((0x[0-9a-f]*)\) 77 | $/msgx 78 | or warn "$bin: unable to parse '$_'\n" 79 | and next; 80 | 81 | next if $lib =~ /^linux-vdso/; 82 | 83 | push @{ $libs{$lib} ||= [] }, $bin; 84 | } 85 | } 86 | 87 | system 'mkdir', '-p', "$lib_dir"; 88 | 89 | for my $lib (sort keys %libs) 90 | { 91 | print "$lib: ", join(' ', @{$libs{$lib}}), "\n"; 92 | print DEPS "\t$lib \\\n"; 93 | 94 | cp($lib, "$lib_dir/" . basename($lib)) 95 | or die "$lib: Unable to copy: $!\n"; 96 | } 97 | 98 | 99 | print DEPS "\n"; 100 | close DEPS; 101 | -------------------------------------------------------------------------------- /initrd/ramdisk-create: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | # Creates a ramdisk image, install it via UEFI ramdisk protocol, 3 | # and then mount it on the destination 4 | # 5 | # ramdisk-create 1024 /ramdisk 6 | 7 | die() { echo >&2 "$*" ; exit 1 ; } 8 | 9 | kilobytes="$1" ; shift 10 | dest="$1" ; shift 11 | 12 | if [ -z "$kilobyte" ]; then 13 | kilobytes=1024 14 | fi 15 | if [ -z "$dest" ]; then 16 | dest="/ramdisk" 17 | fi 18 | 19 | img=/tmp/ramdisk.$$.img 20 | 21 | dd if=/dev/zero bs=1024 count="$kilobytes" of="$img" \ 22 | || die "$img: unable to create" 23 | 24 | # there is probably a better way to do this... 25 | # n - create new partition 26 | # p - primary 27 | # 1 - partition 1 28 | # - accept default start 29 | # - accept default end 30 | # t - set parititon type 31 | # b - win95 fat32 32 | # w - write table to image 33 | fdisk "$img" < /sys/firmware/efi/ramdisk \ 46 | || die "$img: unable to load ramdisk" 47 | sleep 1 48 | 49 | # the image file is no longer required 50 | rm "$img" 51 | 52 | mkdir -p "$dest" \ 53 | || die "$dest: unable to create mount point" 54 | 55 | dev=$(ls /dev/uefi* | tail -1) 56 | if [ -z "$dev" ]; then 57 | die "no uefi devices found?" 58 | fi 59 | 60 | mkfs.vfat "$dev" \ 61 | || die "$dev: unable to format partition" 62 | 63 | mount "$dev" "$dest" \ 64 | || die "$dest: unable to mount $dev" 65 | -------------------------------------------------------------------------------- /kernel/Makefile: -------------------------------------------------------------------------------- 1 | # enable dependency tracking during builds 2 | OLD_SHELL := $(SHELL) 3 | SHELL = $(if $@,$(warning $(OLD_SHELL date "+%Y-%m-%d %H:%M:%S") Building $@$(if $?, ($? newer))))$(OLD_SHELL) 4 | 5 | GIT_DIRTY := $(shell if git status -s >/dev/null ; then echo dirty ; else echo clean ; fi) 6 | GIT_HASH := $(shell git rev-parse HEAD) 7 | TOP := $(shell pwd) 8 | 9 | #O ?= $(TOP)/../build 10 | O ?= ../build 11 | 12 | all: $O/vmlinuz 13 | 14 | $O: 15 | mkdir -p "$@" 16 | 17 | # 18 | # Linux kernel for the PXE boot image 19 | # 20 | LINUX := linux-5.4.117 21 | #LINUX := linux-5.10.100 22 | #LINUX := linux-5.15.23 23 | #LINUX := linux-5.7.2 24 | LINUX_TAR := $(LINUX).tar.xz 25 | LINUX_SIG := $(LINUX).tar.sign 26 | LINUX_URL := https://cdn.kernel.org/pub/linux/kernel/v5.x/$(LINUX_TAR) 27 | 28 | LINUX_PATCH := ../config/$(LINUX).patch 29 | LINUX_CONFIG := ../config/$(LINUX).config 30 | LINUX_DOTCONFIG := $O/$(LINUX)/.config 31 | 32 | $(LINUX_TAR): 33 | [ -r $@.tmp ] || wget -O $@.tmp $(LINUX_URL) 34 | [ -r $(LINUX_SIG) ] || wget -nc $(dir $(LINUX_URL))/$(LINUX_SIG) 35 | #unxz -cd < $@.tmp | gpg2 --verify $(LINUX_SIG) - 36 | mv $@.tmp $@ 37 | 38 | $(LINUX): $(LINUX)/.patched 39 | $(LINUX)/.extract: $(LINUX_TAR) 40 | tar xf $(LINUX_TAR) 41 | touch $@ 42 | $(LINUX)/.patched: $(LINUX_PATCH) $(LINUX)/.extract 43 | patch -p0 < $< 44 | ln -s ../../../module $(LINUX)/drivers/uefidev 45 | touch $@ 46 | 47 | $O/vmlinuz: $(LINUX_DOTCONFIG) $(if $(FORCE),FORCE,) 48 | $(MAKE) \ 49 | KBUILD_HOST=safeboot \ 50 | KBUILD_BUILD_USER=builder \ 51 | KBUILD_BUILD_TIMESTAMP="$(GIT_HASH)" \ 52 | KBUILD_BUILD_VERSION="$(GIT_DIRTY)" \ 53 | -C $O/$(LINUX) 54 | cp $O/$(LINUX)/arch/x86/boot/bzImage $@ 55 | 56 | $(LINUX_DOTCONFIG): $(LINUX_CONFIG) $(LINUX)/.patched 57 | mkdir -p $(dir $@) 58 | cp $< $@ 59 | $(MAKE) \ 60 | -C $(LINUX) \ 61 | O=../$(dir $@) \ 62 | olddefconfig 63 | 64 | menuconfig: $(LINUX_DOTCONFIG) 65 | $(MAKE) -j1 -C $(dir $<) menuconfig savedefconfig 66 | cp $(dir $<)defconfig $(LINUX_CONFIG) 67 | 68 | 69 | $O/clean/$(LINUX)/.extract: $(LINUX_TAR) 70 | mkdir -p $O/clean 71 | tar -xf $(LINUX_TAR) -C $O/clean 72 | touch $@ 73 | 74 | create-patch: $O/clean/$(LINUX)/.extract 75 | -diff \ 76 | -u \ 77 | --recursive \ 78 | $O/clean/$(LINUX) \ 79 | $(LINUX) \ 80 | | grep -v '^Only in ' \ 81 | | tee $(LINUX_PATCH) 82 | 83 | FORCE: 84 | -------------------------------------------------------------------------------- /kernel/dev.cpio: -------------------------------------------------------------------------------- 1 | 0707015907caa3000021800000000000000000000000000000000000000000000000000000000000000005000000010000000C00000000dev/console07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!! -------------------------------------------------------------------------------- /module/Kconfig: -------------------------------------------------------------------------------- 1 | menuconfig UEFIDEV 2 | tristate "UEFI Device Drivers" 3 | depends on EFI 4 | default n 5 | ---help--- 6 | This option adds support for using UEFI device drivers as 7 | part of the Linux kernel. It probably is not what you want. 8 | if UEFIDEV 9 | 10 | config UEFINET 11 | bool "UEFI Network Driver" 12 | depends on NETDEVICES 13 | ---help--- 14 | Enable EFI_SIMPLE_NETWORK_PROTOCOL devices to show up as ethN. 15 | The performance isn't super, but it allows the Linux kernel to 16 | send and receive packets over the UEFI firmware provided 17 | network interface without touching PCI or disturbing other 18 | system state. 19 | 20 | config UEFIBLOCK 21 | bool "UEFI Block Devices" 22 | depends on BLOCK 23 | ---help--- 24 | Enable EFI_BLOCK_IO_PROTOCOL devices to show up as /dev/uefiN. 25 | The performance isn't super, but it allows the Linux kernel to 26 | read and write the physical disks or RAM disks without touching 27 | PCI or disturbing other system state. 28 | 29 | config UEFITPM 30 | bool "UEFI TPM Devices" 31 | depends on TCG_TPM 32 | ---help--- 33 | Enable EFI_TCG2_PROTOCOL devices to show up as /dev/tpmN. 34 | This interface doesn't allow overlapping commands or anything 35 | fancy, so it may have problems. 36 | 37 | endif # UEFDEV 38 | -------------------------------------------------------------------------------- /module/Makefile: -------------------------------------------------------------------------------- 1 | ifneq ($(KERNELRELEASE),) 2 | # kbuild part of makefile 3 | obj-$(CONFIG_UEFIDEV) := uefidev.o 4 | uefidev-objs += main.o 5 | uefidev-objs += efiwrapper.o 6 | uefidev-objs += event.o 7 | uefidev-objs += loader.o 8 | uefidev-objs += ramdisk.o 9 | uefidev-$(CONFIG_UEFINET) += efinet.o 10 | uefidev-$(CONFIG_UEFIBLOCK) += blockio.o 11 | uefidev-$(CONFIG_UEFITPM) += tpm.o 12 | 13 | ccflags-y += -std=gnu99 14 | #ccflags-y += -DGNU_EFI_USE_MS_ABI 15 | #ccflags-y += -I$(src)/include 16 | #ccflags-y += -I/usr/include/efi 17 | #ccflags-y += -I/usr/include/efi/x86_64 18 | 19 | else 20 | # normal makefile 21 | #KDIR ?= /lib/modules/`uname -r`/build 22 | KDIR ?= ../build/linux-5.4.117 23 | 24 | default: 25 | $(MAKE) \ 26 | -C $(KDIR) \ 27 | M=$$PWD \ 28 | CONFIG_UEFIDEV=m \ 29 | 30 | # Module specific targets 31 | genbin: 32 | #echo "X" > 8123_bin.o_shipped 33 | 34 | clean: 35 | $(RM) \ 36 | Module.symvers modules.order \ 37 | *.o *.a *.ko *.mod *.mod.* .*.cmd 38 | 39 | endif 40 | -------------------------------------------------------------------------------- /module/Tcg2Protocol.h: -------------------------------------------------------------------------------- 1 | /** @file 2 | TPM2 Protocol as defined in TCG PC Client Platform EFI Protocol Specification Family "2.0". 3 | See http://trustedcomputinggroup.org for the latest specification 4 | 5 | Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
6 | SPDX-License-Identifier: BSD-2-Clause-Patent 7 | 8 | **/ 9 | 10 | #ifndef __TCG2_PROTOCOL_H__ 11 | #define __TCG2_PROTOCOL_H__ 12 | 13 | //#include 14 | //#include 15 | 16 | typedef uint32_t TCG_PCRINDEX; 17 | typedef uint32_t TCG_EVENTTYPE; 18 | 19 | //#define EFI_TCG2_PROTOCOL_GUID EFI_GUID(0x607f766c, 0x7455, 0x42be, 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f ) 20 | 21 | typedef struct tdEFI_TCG2_PROTOCOL EFI_TCG2_PROTOCOL; 22 | 23 | typedef struct tdEFI_TCG2_VERSION { 24 | UINT8 Major; 25 | UINT8 Minor; 26 | } EFI_TCG2_VERSION; 27 | 28 | typedef UINT32 EFI_TCG2_EVENT_LOG_BITMAP; 29 | typedef UINT32 EFI_TCG2_EVENT_LOG_FORMAT; 30 | typedef UINT32 EFI_TCG2_EVENT_ALGORITHM_BITMAP; 31 | 32 | #ifndef EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2 33 | #define EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2 0x00000001 34 | #define EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 0x00000002 35 | #endif 36 | 37 | typedef struct tdEFI_TCG2_BOOT_SERVICE_CAPABILITY { 38 | // 39 | // Allocated size of the structure 40 | // 41 | UINT8 Size; 42 | // 43 | // Version of the EFI_TCG2_BOOT_SERVICE_CAPABILITY structure itself. 44 | // For this version of the protocol, the Major version shall be set to 1 45 | // and the Minor version shall be set to 1. 46 | // 47 | EFI_TCG2_VERSION StructureVersion; 48 | // 49 | // Version of the EFI TCG2 protocol. 50 | // For this version of the protocol, the Major version shall be set to 1 51 | // and the Minor version shall be set to 1. 52 | // 53 | EFI_TCG2_VERSION ProtocolVersion; 54 | // 55 | // Supported hash algorithms (this bitmap is determined by the supported PCR 56 | // banks in the TPM and the hashing algorithms supported by the firmware) 57 | // 58 | EFI_TCG2_EVENT_ALGORITHM_BITMAP HashAlgorithmBitmap; 59 | // 60 | // Bitmap of supported event log formats 61 | // 62 | EFI_TCG2_EVENT_LOG_BITMAP SupportedEventLogs; 63 | // 64 | // False = TPM not present 65 | // 66 | BOOLEAN TPMPresentFlag; 67 | // 68 | // Max size (in bytes) of a command that can be sent to the TPM 69 | // 70 | UINT16 MaxCommandSize; 71 | // 72 | // Max size (in bytes) of a response that can be provided by the TPM 73 | // 74 | UINT16 MaxResponseSize; 75 | // 76 | // 4-byte Vendor ID 77 | // (see TCG Vendor ID registry, Section "TPM Capabilities Vendor ID") 78 | // 79 | UINT32 ManufacturerID; 80 | // 81 | // Maximum number of PCR banks (hashing algorithms) supported. 82 | // No granularity is provided to support a specific set of algorithms. 83 | // Minimum value is 1. 84 | // 85 | UINT32 NumberOfPCRBanks; 86 | // 87 | // A bitmap of currently active PCR banks (hashing algorithms). 88 | // This is a subset of the supported hashing algorithms reported in HashAlgorithmBitMap. 89 | // NumberOfPcrBanks defines the number of bits that are set. 90 | // 91 | EFI_TCG2_EVENT_ALGORITHM_BITMAP ActivePcrBanks; 92 | } EFI_TCG2_BOOT_SERVICE_CAPABILITY; 93 | 94 | #define EFI_TCG2_BOOT_HASH_ALG_SHA1 0x00000001 95 | #define EFI_TCG2_BOOT_HASH_ALG_SHA256 0x00000002 96 | #define EFI_TCG2_BOOT_HASH_ALG_SHA384 0x00000004 97 | #define EFI_TCG2_BOOT_HASH_ALG_SHA512 0x00000008 98 | #define EFI_TCG2_BOOT_HASH_ALG_SM3_256 0x00000010 99 | 100 | // 101 | // This bit is shall be set when an event shall be extended but not logged. 102 | // 103 | #define EFI_TCG2_EXTEND_ONLY 0x0000000000000001 104 | // 105 | // This bit shall be set when the intent is to measure a PE/COFF image. 106 | // 107 | #define PE_COFF_IMAGE 0x0000000000000010 108 | 109 | #define MAX_PCR_INDEX 23 110 | 111 | #pragma pack(1) 112 | 113 | #define EFI_TCG2_EVENT_HEADER_VERSION 1 114 | 115 | typedef struct { 116 | // 117 | // Size of the event header itself (sizeof(EFI_TCG2_EVENT_HEADER)). 118 | // 119 | UINT32 HeaderSize; 120 | // 121 | // Header version. For this version of this specification, the value shall be 1. 122 | // 123 | UINT16 HeaderVersion; 124 | // 125 | // Index of the PCR that shall be extended (0 - 23). 126 | // 127 | TCG_PCRINDEX PCRIndex; 128 | // 129 | // Type of the event that shall be extended (and optionally logged). 130 | // 131 | TCG_EVENTTYPE EventType; 132 | } EFI_TCG2_EVENT_HEADER; 133 | 134 | typedef struct tdEFI_TCG2_EVENT { 135 | // 136 | // Total size of the event including the Size component, the header and the Event data. 137 | // 138 | UINT32 Size; 139 | EFI_TCG2_EVENT_HEADER Header; 140 | UINT8 Event[1]; 141 | } EFI_TCG2_EVENT; 142 | 143 | #pragma pack() 144 | 145 | /** 146 | The EFI_TCG2_PROTOCOL GetCapability function call provides protocol 147 | capability information and state information. 148 | 149 | @param[in] This Indicates the calling context 150 | @param[in, out] ProtocolCapability The caller allocates memory for a EFI_TCG2_BOOT_SERVICE_CAPABILITY 151 | structure and sets the size field to the size of the structure allocated. 152 | The callee fills in the fields with the EFI protocol capability information 153 | and the current EFI TCG2 state information up to the number of fields which 154 | fit within the size of the structure passed in. 155 | 156 | @retval EFI_SUCCESS Operation completed successfully. 157 | @retval EFI_DEVICE_ERROR The command was unsuccessful. 158 | The ProtocolCapability variable will not be populated. 159 | @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. 160 | The ProtocolCapability variable will not be populated. 161 | @retval EFI_BUFFER_TOO_SMALL The ProtocolCapability variable is too small to hold the full response. 162 | It will be partially populated (required Size field will be set). 163 | **/ 164 | typedef 165 | EFI_STATUS 166 | (EFIAPI *EFI_TCG2_GET_CAPABILITY) ( 167 | IN EFI_TCG2_PROTOCOL *This, 168 | IN OUT EFI_TCG2_BOOT_SERVICE_CAPABILITY *ProtocolCapability 169 | ); 170 | 171 | /** 172 | The EFI_TCG2_PROTOCOL Get Event Log function call allows a caller to 173 | retrieve the address of a given event log and its last entry. 174 | 175 | @param[in] This Indicates the calling context 176 | @param[in] EventLogFormat The type of the event log for which the information is requested. 177 | @param[out] EventLogLocation A pointer to the memory address of the event log. 178 | @param[out] EventLogLastEntry If the Event Log contains more than one entry, this is a pointer to the 179 | address of the start of the last entry in the event log in memory. 180 | @param[out] EventLogTruncated If the Event Log is missing at least one entry because an event would 181 | have exceeded the area allocated for events, this value is set to TRUE. 182 | Otherwise, the value will be FALSE and the Event Log will be complete. 183 | 184 | @retval EFI_SUCCESS Operation completed successfully. 185 | @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect 186 | (e.g. asking for an event log whose format is not supported). 187 | **/ 188 | typedef 189 | EFI_STATUS 190 | (EFIAPI *EFI_TCG2_GET_EVENT_LOG) ( 191 | IN EFI_TCG2_PROTOCOL *This, 192 | IN EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat, 193 | OUT EFI_PHYSICAL_ADDRESS *EventLogLocation, 194 | OUT EFI_PHYSICAL_ADDRESS *EventLogLastEntry, 195 | OUT BOOLEAN *EventLogTruncated 196 | ); 197 | 198 | /** 199 | The EFI_TCG2_PROTOCOL HashLogExtendEvent function call provides callers with 200 | an opportunity to extend and optionally log events without requiring 201 | knowledge of actual TPM commands. 202 | The extend operation will occur even if this function cannot create an event 203 | log entry (e.g. due to the event log being full). 204 | 205 | @param[in] This Indicates the calling context 206 | @param[in] Flags Bitmap providing additional information. 207 | @param[in] DataToHash Physical address of the start of the data buffer to be hashed. 208 | @param[in] DataToHashLen The length in bytes of the buffer referenced by DataToHash. 209 | @param[in] EfiTcgEvent Pointer to data buffer containing information about the event. 210 | 211 | @retval EFI_SUCCESS Operation completed successfully. 212 | @retval EFI_DEVICE_ERROR The command was unsuccessful. 213 | @retval EFI_VOLUME_FULL The extend operation occurred, but the event could not be written to one or more event logs. 214 | @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. 215 | @retval EFI_UNSUPPORTED The PE/COFF image type is not supported. 216 | **/ 217 | typedef 218 | EFI_STATUS 219 | (EFIAPI * EFI_TCG2_HASH_LOG_EXTEND_EVENT) ( 220 | IN EFI_TCG2_PROTOCOL *This, 221 | IN UINT64 Flags, 222 | IN EFI_PHYSICAL_ADDRESS DataToHash, 223 | IN UINT64 DataToHashLen, 224 | IN EFI_TCG2_EVENT *EfiTcgEvent 225 | ); 226 | 227 | /** 228 | This service enables the sending of commands to the TPM. 229 | 230 | @param[in] This Indicates the calling context 231 | @param[in] InputParameterBlockSize Size of the TPM input parameter block. 232 | @param[in] InputParameterBlock Pointer to the TPM input parameter block. 233 | @param[in] OutputParameterBlockSize Size of the TPM output parameter block. 234 | @param[in] OutputParameterBlock Pointer to the TPM output parameter block. 235 | 236 | @retval EFI_SUCCESS The command byte stream was successfully sent to the device and a response was successfully received. 237 | @retval EFI_DEVICE_ERROR The command was not successfully sent to the device or a response was not successfully received from the device. 238 | @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. 239 | @retval EFI_BUFFER_TOO_SMALL The output parameter block is too small. 240 | **/ 241 | typedef 242 | EFI_STATUS 243 | (EFIAPI *EFI_TCG2_SUBMIT_COMMAND) ( 244 | IN EFI_TCG2_PROTOCOL *This, 245 | IN UINT32 InputParameterBlockSize, 246 | IN UINT8 *InputParameterBlock, 247 | IN UINT32 OutputParameterBlockSize, 248 | IN UINT8 *OutputParameterBlock 249 | ); 250 | 251 | /** 252 | This service returns the currently active PCR banks. 253 | 254 | @param[in] This Indicates the calling context 255 | @param[out] ActivePcrBanks Pointer to the variable receiving the bitmap of currently active PCR banks. 256 | 257 | @retval EFI_SUCCESS The bitmap of active PCR banks was stored in the ActivePcrBanks parameter. 258 | @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. 259 | **/ 260 | typedef 261 | EFI_STATUS 262 | (EFIAPI *EFI_TCG2_GET_ACTIVE_PCR_BANKS) ( 263 | IN EFI_TCG2_PROTOCOL *This, 264 | OUT UINT32 *ActivePcrBanks 265 | ); 266 | 267 | /** 268 | This service sets the currently active PCR banks. 269 | 270 | @param[in] This Indicates the calling context 271 | @param[in] ActivePcrBanks Bitmap of the requested active PCR banks. At least one bit SHALL be set. 272 | 273 | @retval EFI_SUCCESS The bitmap in ActivePcrBank parameter is already active. 274 | @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. 275 | **/ 276 | typedef 277 | EFI_STATUS 278 | (EFIAPI *EFI_TCG2_SET_ACTIVE_PCR_BANKS) ( 279 | IN EFI_TCG2_PROTOCOL *This, 280 | IN UINT32 ActivePcrBanks 281 | ); 282 | 283 | /** 284 | This service retrieves the result of a previous invocation of SetActivePcrBanks. 285 | 286 | @param[in] This Indicates the calling context 287 | @param[out] OperationPresent Non-zero value to indicate a SetActivePcrBank operation was invoked during the last boot. 288 | @param[out] Response The response from the SetActivePcrBank request. 289 | 290 | @retval EFI_SUCCESS The result value could be returned. 291 | @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. 292 | **/ 293 | typedef 294 | EFI_STATUS 295 | (EFIAPI *EFI_TCG2_GET_RESULT_OF_SET_ACTIVE_PCR_BANKS) ( 296 | IN EFI_TCG2_PROTOCOL *This, 297 | OUT UINT32 *OperationPresent, 298 | OUT UINT32 *Response 299 | ); 300 | 301 | struct tdEFI_TCG2_PROTOCOL { 302 | EFI_TCG2_GET_CAPABILITY GetCapability; 303 | EFI_TCG2_GET_EVENT_LOG GetEventLog; 304 | EFI_TCG2_HASH_LOG_EXTEND_EVENT HashLogExtendEvent; 305 | EFI_TCG2_SUBMIT_COMMAND SubmitCommand; 306 | EFI_TCG2_GET_ACTIVE_PCR_BANKS GetActivePcrBanks; 307 | EFI_TCG2_SET_ACTIVE_PCR_BANKS SetActivePcrBanks; 308 | EFI_TCG2_GET_RESULT_OF_SET_ACTIVE_PCR_BANKS GetResultOfSetActivePcrBanks; 309 | }; 310 | 311 | extern EFI_GUID gEfiTcg2ProtocolGuid; 312 | 313 | // 314 | // Log entries after Get Event Log service 315 | // 316 | 317 | #define EFI_TCG2_FINAL_EVENTS_TABLE_GUID \ 318 | {0x1e2ed096, 0x30e2, 0x4254, { 0xbd, 0x89, 0x86, 0x3b, 0xbe, 0xf8, 0x23, 0x25 }} 319 | 320 | extern EFI_GUID gEfiTcg2FinalEventsTableGuid; 321 | 322 | typedef struct tdEFI_TCG2_FINAL_EVENTS_TABLE { 323 | // 324 | // The version of this structure. 325 | // 326 | UINT64 Version; 327 | // 328 | // Number of events recorded after invocation of GetEventLog API 329 | // 330 | UINT64 NumberOfEvents; 331 | // 332 | // List of events of type TCG_PCR_EVENT2. 333 | // 334 | //TCG_PCR_EVENT2 Event[1]; 335 | } EFI_TCG2_FINAL_EVENTS_TABLE; 336 | 337 | #define EFI_TCG2_FINAL_EVENTS_TABLE_VERSION 1 338 | 339 | #endif 340 | -------------------------------------------------------------------------------- /module/blockio.c: -------------------------------------------------------------------------------- 1 | /** UEFI Block Device. 2 | * 3 | * Implements a simplistic block device that calls back 4 | * into the UEFI BlockDeviceProtocol. This assumes that 5 | * the uefi memory map has already been setup 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "efiwrapper.h" 14 | #include "blockio.h" 15 | 16 | static int debug = 0; 17 | static int major; 18 | 19 | 20 | typedef struct { 21 | spinlock_t lock; 22 | struct gendisk *gd; 23 | struct request_queue *queue; 24 | struct blk_mq_tag_set tag_set; 25 | atomic_t refcnt; 26 | 27 | EFI_HANDLE uefi_handle; 28 | EFI_BLOCK_IO_PROTOCOL *uefi_bio; 29 | uint8_t * buffer; 30 | 31 | char devicepath_string[256]; 32 | } uefi_blockdev_t; 33 | 34 | static blk_status_t uefi_blockdev_request(struct blk_mq_hw_ctx * hctx, const struct blk_mq_queue_data * bd) 35 | { 36 | struct request * rq = bd->rq; 37 | uefi_blockdev_t * dev = rq->rq_disk->private_data; 38 | struct bio_vec bvec; 39 | struct req_iterator iter; 40 | int status = 0; 41 | 42 | uefi_memory_map_add(); 43 | 44 | if (blk_rq_is_passthrough(rq)) 45 | { 46 | printk(KERN_NOTICE "skip non-fs request\n"); 47 | blk_mq_end_request(rq, BLK_STS_IOERR); 48 | return BLK_STS_OK; 49 | } 50 | 51 | rq_for_each_segment(bvec, rq, iter) { 52 | // sector is *always* in Linux 512 blocks 53 | sector_t sector = iter.iter.bi_sector; 54 | size_t len = bvec.bv_len; 55 | char * buffer = kmap_atomic(bvec.bv_page); 56 | unsigned long offset = bvec.bv_offset; 57 | const bool is_write = bio_data_dir(iter.bio); 58 | const size_t bs = dev->uefi_bio->Media->BlockSize; 59 | const unsigned long disk_addr = sector * 512; 60 | const unsigned long disk_sector = disk_addr / bs; 61 | 62 | void * dest = buffer + offset; 63 | 64 | // how many full blocks do we have? 65 | size_t full_block_len = len & ~(bs - 1); 66 | size_t partial_block_len = len - full_block_len; 67 | 68 | // read and write have the same signature 69 | EFI_BLOCK_READ handler = is_write 70 | ? dev->uefi_bio->WriteBlocks 71 | : dev->uefi_bio->ReadBlocks; 72 | 73 | if (debug) 74 | printk("%s.%d: %s %08llx %08lx %08lx => %08llx + %08llx @ %08llx + %08llx\n", 75 | dev->gd->disk_name, 76 | dev->uefi_bio->Media->MediaId, 77 | bio_data_dir(iter.bio) ? "WRITE" : "READ ", 78 | sector, 79 | disk_addr, 80 | disk_sector, 81 | (uint64_t) buffer, 82 | (uint64_t) offset, 83 | (uint64_t) full_block_len, 84 | (uint64_t) partial_block_len 85 | ); 86 | 87 | // do an operation on as many full blocks as we can 88 | if (full_block_len != 0) 89 | status |= handler( 90 | dev->uefi_bio, 91 | dev->uefi_bio->Media->MediaId, 92 | disk_sector, 93 | full_block_len, 94 | dest 95 | ); 96 | 97 | // and fix up any stragglers with our bounce buffer 98 | if (partial_block_len != 0) 99 | { 100 | const size_t full_blocks = full_block_len / bs; 101 | printk("tail %zu blocks + %zu bytes\n", full_blocks, partial_block_len); 102 | 103 | // pass through our block sized buffer 104 | if (is_write) 105 | { 106 | printk("%s: short write %llx\n", dev->gd->disk_name, (uint64_t) partial_block_len); 107 | memcpy(dev->buffer, dest, partial_block_len); 108 | } 109 | 110 | status |= handler( 111 | dev->uefi_bio, 112 | dev->uefi_bio->Media->MediaId, 113 | disk_sector + full_blocks, 114 | bs, // full block operation 115 | dev->buffer 116 | ); 117 | 118 | if (!is_write) 119 | { 120 | // copy the desired amount out of our buffer 121 | memcpy(dest + full_block_len, dev->buffer, partial_block_len); 122 | } 123 | } 124 | 125 | kunmap_atomic(buffer); 126 | } 127 | 128 | /* // todo: figure out what replaced this 129 | // force writes if this is a barrier request 130 | if (blk_barrier_rq(rq)) 131 | efi_call(dev->uefi_bio->FlushBlocks, dev->uefi_bio); 132 | */ 133 | 134 | if (status) 135 | printk("%s: operation failed %x\n", dev->gd->disk_name, status); 136 | 137 | blk_mq_end_request(rq, status ? BLK_STS_IOERR : 0); 138 | return BLK_STS_OK; 139 | } 140 | 141 | static struct blk_mq_ops uefi_blockdev_qops = { 142 | .queue_rq = uefi_blockdev_request, 143 | }; 144 | 145 | static int uefi_blockdev_open(struct block_device * bd, fmode_t mode) 146 | { 147 | uefi_blockdev_t * const dev = bd->bd_disk->private_data; 148 | atomic_inc(&dev->refcnt); 149 | //printk("opened '%s'\n", bd->bd_disk->disk_name); 150 | return 0; 151 | } 152 | 153 | static void uefi_blockdev_release(struct gendisk * disk, fmode_t mode) 154 | { 155 | uefi_blockdev_t * const dev = disk->private_data; 156 | atomic_dec(&dev->refcnt); 157 | } 158 | 159 | /* // todo: support removable media 160 | static int uefi_blockdev_media_changed(struct gendisk * gd) 161 | { 162 | uefi_blockdev_t * const dev = gd->private_data; 163 | return dev->uefi_bio->Media->MediaPresent; 164 | } 165 | */ 166 | 167 | /* // todo: support ioctl 168 | static int uefi_blockdev_ioctl(struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg) 169 | { 170 | uefi_blockdev_t * const dev = inode->i_bdev->bd_disk->private_data; 171 | struct hd_geometry geo = { 172 | .heads = 4, 173 | .sectors = 16, 174 | .start = 0, 175 | .cylinders = dev->uefi_bio->Media->LastBlock / 4 / 16, 176 | }; 177 | 178 | switch(cmd) { 179 | case HDIO_GETGEO: 180 | if (copy_to_user((void __user *) arg, &geo, sizeof(geo))) 181 | return -EFAULT; 182 | return 0; 183 | } 184 | 185 | return -ENOTTY; 186 | } 187 | */ 188 | 189 | 190 | static struct block_device_operations uefi_blockdev_fops = { 191 | .owner = THIS_MODULE, 192 | .open = uefi_blockdev_open, 193 | .release = uefi_blockdev_release, 194 | // .ioctl = uefi_blockdev_ioctl, 195 | // .media_changed = uefi_blockdev_media_changed, 196 | }; 197 | 198 | 199 | static ssize_t sysfs_devpath_show(struct device * dev, struct device_attribute * attr, char * buf) 200 | { 201 | struct gendisk * disk = dev_to_disk(dev); 202 | uefi_blockdev_t * uefi = disk->private_data; 203 | sprintf(buf, "%s\n", uefi->devicepath_string); 204 | return strlen(buf); 205 | } 206 | 207 | static ssize_t sysfs_handle_show(struct device * dev, struct device_attribute * attr, char * buf) 208 | { 209 | struct gendisk * disk = dev_to_disk(dev); 210 | uefi_blockdev_t * uefi = disk->private_data; 211 | sprintf(buf, "0x%016llx\n", (uint64_t) uefi->uefi_handle); 212 | return strlen(buf); 213 | } 214 | 215 | static DEVICE_ATTR(uefi_devicepath, 0444, sysfs_devpath_show, NULL); 216 | static DEVICE_ATTR(uefi_handle, 0444, sysfs_handle_show, NULL); 217 | 218 | struct attribute * uefi_blockdev_attrs[] = { 219 | &dev_attr_uefi_devicepath.attr, 220 | &dev_attr_uefi_handle.attr, 221 | NULL, 222 | }; 223 | 224 | 225 | static void * uefi_blockdev_add(int minor, EFI_HANDLE handle, EFI_BLOCK_IO_PROTOCOL * uefi_bio) 226 | { 227 | const EFI_BLOCK_IO_MEDIA * const media = uefi_bio->Media; 228 | struct gendisk * disk; 229 | struct kobject * disk_kobj; 230 | uefi_blockdev_t * dev; 231 | void * fs; 232 | const char * devpath = uefi_device_path_to_name(handle); 233 | 234 | printk("uefi%d: %s\n", minor, devpath); 235 | 236 | fs = uefi_handle_protocol(&EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, handle); 237 | printk("uefi%d: rev=%llx id=%d removable=%d present=%d logical=%d ro=%d caching=%d bs=%u size=%llu%s\n", 238 | minor, 239 | uefi_bio->Revision, 240 | media->MediaId, 241 | media->RemovableMedia, 242 | media->MediaPresent, 243 | media->LogicalPartition, 244 | media->ReadOnly, 245 | media->WriteCaching, 246 | media->BlockSize, 247 | media->LastBlock * media->BlockSize, 248 | fs ? " SIMPLE_FS" : ""); 249 | 250 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); 251 | if (!dev) 252 | return NULL; 253 | 254 | spin_lock_init(&dev->lock); 255 | atomic_set(&dev->refcnt, 0); 256 | dev->uefi_bio = uefi_bio; 257 | dev->buffer = kzalloc(media->BlockSize, GFP_KERNEL); 258 | dev->uefi_handle = handle; 259 | strncpy(dev->devicepath_string, devpath, sizeof(dev->devicepath_string)); 260 | 261 | //disk = dev->gd = blk_alloc_disk(0); // 5.15 262 | disk = dev->gd = alloc_disk(1); // 5.4 263 | if (!disk) 264 | return NULL; 265 | 266 | sprintf(disk->disk_name, "uefi%d", minor); 267 | 268 | disk->private_data = dev; 269 | disk->major = major; 270 | disk->first_minor = minor; 271 | disk->minors = 1; 272 | // todo: support removable media 273 | //disk->flags = media->RemovableMedia ? GENHD_FL_REMOVABLE : 0; 274 | disk->fops = &uefi_blockdev_fops; 275 | 276 | dev->tag_set.ops = &uefi_blockdev_qops; 277 | dev->tag_set.nr_hw_queues = 1; 278 | dev->tag_set.queue_depth = 128; // should be 1? 279 | dev->tag_set.numa_node = NUMA_NO_NODE; 280 | dev->tag_set.cmd_size = 0; 281 | dev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; 282 | 283 | blk_mq_alloc_tag_set(&dev->tag_set); 284 | 285 | disk->queue = dev->queue = blk_mq_init_queue(&dev->tag_set); 286 | dev->queue->queuedata = dev; 287 | 288 | blk_queue_logical_block_size(dev->queue, media->BlockSize); 289 | set_capacity(disk, media->LastBlock * (media->BlockSize / 512)); // in Linux sectors 290 | 291 | add_disk(disk); 292 | 293 | // try to create the sysfs files once add_disk() has created 294 | // the /sys entries for this block device. 295 | disk_kobj = get_disk_and_module(disk); 296 | 297 | for(struct attribute ** attr = uefi_blockdev_attrs ; *attr ; attr++) 298 | { 299 | if (sysfs_create_file(disk_kobj, *attr) < 0) 300 | { 301 | // what are you going to do? 302 | printk("%s: unable to create sysfs entry\n", disk->disk_name); 303 | } 304 | } 305 | 306 | 307 | return dev; 308 | } 309 | 310 | static int uefi_blockdev_handle_done(EFI_HANDLE handle) 311 | { 312 | static EFI_HANDLE handles_done[64]; 313 | static int handles_done_count; 314 | 315 | for(unsigned i = 0 ; i < handles_done_count ; i++) 316 | { 317 | if (handles_done[i] == handle) 318 | return 1; 319 | } 320 | 321 | // this one is new, add it to our lsit 322 | handles_done[handles_done_count++] = handle; 323 | return 0; 324 | } 325 | 326 | // called when there is a new block device driver registered 327 | // it unfortunately finds all of them. 328 | static void uefi_blockdev_scan(void * unused) 329 | { 330 | EFI_HANDLE handles[64]; 331 | int handle_count = uefi_locate_handles(&EFI_BLOCK_IO_PROTOCOL_GUID, handles, 64); 332 | int count = 0; 333 | 334 | if (handle_count < 1) 335 | return; 336 | 337 | for(unsigned i = 0 ; i < handle_count ; i++) 338 | { 339 | EFI_HANDLE handle = handles[i]; 340 | EFI_BLOCK_IO_PROTOCOL * uefi_bio; 341 | 342 | // have we seen this one? 343 | if (uefi_blockdev_handle_done(handle)) 344 | continue; 345 | 346 | uefi_bio = uefi_handle_protocol( 347 | &EFI_BLOCK_IO_PROTOCOL_GUID, 348 | handle 349 | ); 350 | 351 | if (!uefi_bio) 352 | continue; 353 | 354 | uefi_blockdev_add(i, handle, uefi_bio); 355 | count++; 356 | } 357 | 358 | printk("uefi_blockdev: created %d block devices\n", count); 359 | } 360 | 361 | 362 | static int uefi_blockdev_register(void) 363 | { 364 | uefi_register_protocol_callback( 365 | &EFI_BLOCK_IO_PROTOCOL_GUID, 366 | uefi_blockdev_scan, 367 | NULL 368 | ); 369 | return 0; 370 | } 371 | 372 | int uefi_blockdev_init(void) 373 | { 374 | major = register_blkdev(0, DRIVER_NAME); 375 | if (major < 0) 376 | return -EIO; 377 | 378 | /* 379 | if (uefi_blockdev_scan() < 0) 380 | return -EIO; 381 | */ 382 | 383 | // register the call back and initiate a scan 384 | if (uefi_blockdev_register() < 0) 385 | return -EIO; 386 | 387 | return 0; 388 | } 389 | -------------------------------------------------------------------------------- /module/blockio.h: -------------------------------------------------------------------------------- 1 | #ifndef _efi_blockio_h_ 2 | #define _efi_blockio_h_ 3 | 4 | // 5 | // Block IO protocol 6 | // 7 | #include "efiwrapper.h" 8 | 9 | #define EFI_BLOCK_IO_PROTOCOL_GUID EFI_GUID(0x964e5b21, 0x6459, 0x11d2, 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b) 10 | 11 | #define EFI_BLOCK_IO_PROTOCOL_REVISION 0x00010000 12 | #define EFI_BLOCK_IO_PROTOCOL_REVISION2 0x00020001 13 | #define EFI_BLOCK_IO_PROTOCOL_REVISION3 ((2<<16) | 31) 14 | #define EFI_BLOCK_IO_INTERFACE_REVISION EFI_BLOCK_IO_PROTOCOL_REVISION 15 | #define EFI_BLOCK_IO_INTERFACE_REVISION2 EFI_BLOCK_IO_PROTOCOL_REVISION2 16 | #define EFI_BLOCK_IO_INTERFACE_REVISION3 EFI_BLOCK_IO_PROTOCOL_REVISION3 17 | 18 | 19 | struct _EFI_BLOCK_IO_PROTOCOL; 20 | 21 | 22 | typedef 23 | EFI_STATUS 24 | (EFIAPI *EFI_BLOCK_RESET) ( 25 | IN struct _EFI_BLOCK_IO_PROTOCOL *This, 26 | IN BOOLEAN ExtendedVerification 27 | ); 28 | 29 | typedef 30 | EFI_STATUS 31 | (EFIAPI *EFI_BLOCK_READ) ( 32 | IN struct _EFI_BLOCK_IO_PROTOCOL *This, 33 | IN UINT32 MediaId, 34 | IN EFI_LBA LBA, 35 | IN UINTN BufferSize, 36 | OUT VOID *Buffer 37 | ); 38 | 39 | 40 | typedef 41 | EFI_STATUS 42 | (EFIAPI *EFI_BLOCK_WRITE) ( 43 | IN struct _EFI_BLOCK_IO_PROTOCOL *This, 44 | IN UINT32 MediaId, 45 | IN EFI_LBA LBA, 46 | IN UINTN BufferSize, 47 | IN VOID *Buffer 48 | ); 49 | 50 | 51 | typedef 52 | EFI_STATUS 53 | (EFIAPI *EFI_BLOCK_FLUSH) ( 54 | IN struct _EFI_BLOCK_IO_PROTOCOL *This 55 | ); 56 | 57 | 58 | 59 | typedef struct { 60 | UINT32 MediaId; 61 | BOOLEAN RemovableMedia; 62 | BOOLEAN MediaPresent; 63 | 64 | BOOLEAN LogicalPartition; 65 | BOOLEAN ReadOnly; 66 | BOOLEAN WriteCaching; 67 | 68 | UINT32 BlockSize; 69 | UINT32 IoAlign; 70 | 71 | EFI_LBA LastBlock; 72 | 73 | /* revision 2 */ 74 | EFI_LBA LowestAlignedLba; 75 | UINT32 LogicalBlocksPerPhysicalBlock; 76 | /* revision 3 */ 77 | UINT32 OptimalTransferLengthGranularity; 78 | } EFI_BLOCK_IO_MEDIA; 79 | 80 | typedef struct _EFI_BLOCK_IO_PROTOCOL { 81 | uint64_t Revision; 82 | 83 | EFI_BLOCK_IO_MEDIA *Media; 84 | 85 | EFI_BLOCK_RESET Reset; 86 | EFI_BLOCK_READ ReadBlocks; 87 | EFI_BLOCK_WRITE WriteBlocks; 88 | EFI_BLOCK_FLUSH FlushBlocks; 89 | 90 | } EFI_BLOCK_IO_PROTOCOL; 91 | 92 | typedef struct _EFI_BLOCK_IO_PROTOCOL _EFI_BLOCK_IO; 93 | typedef EFI_BLOCK_IO_PROTOCOL EFI_BLOCK_IO; 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /module/efidhcp4.h: -------------------------------------------------------------------------------- 1 | /** @file 2 | EFI_DHCP4_PROTOCOL as defined in UEFI 2.0. 3 | EFI_DHCP4_SERVICE_BINDING_PROTOCOL as defined in UEFI 2.0. 4 | These protocols are used to collect configuration information for the EFI IPv4 Protocol 5 | drivers and to provide DHCPv4 server and PXE boot server discovery services. 6 | 7 | Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
8 | SPDX-License-Identifier: BSD-2-Clause-Patent 9 | 10 | @par Revision Reference: 11 | This Protocol was introduced in UEFI Specification 2.0. 12 | 13 | **/ 14 | 15 | #ifndef __EFI_DHCP4_PROTOCOL_H__ 16 | #define __EFI_DHCP4_PROTOCOL_H__ 17 | 18 | #define EFI_DHCP4_PROTOCOL_GUID EFI_GUID(0x8a219718, 0x4ef5, 0x4761, 0x91, 0xc8, 0xc0, 0xf0, 0x4b, 0xda, 0x9e, 0x56) 19 | 20 | #define EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID EFI_GUID(0x9d9a39d8, 0xbd42, 0x4a73, 0xa4, 0xd5, 0x8e, 0xe9, 0x4b, 0xe1, 0x13, 0x80) 21 | 22 | typedef struct _EFI_DHCP4_PROTOCOL EFI_DHCP4_PROTOCOL; 23 | 24 | 25 | #pragma pack(1) 26 | typedef struct { 27 | /// 28 | /// DHCP option code. 29 | /// 30 | UINT8 OpCode; 31 | /// 32 | /// Length of the DHCP option data. Not present if OpCode is 0 or 255. 33 | /// 34 | UINT8 Length; 35 | /// 36 | /// Start of the DHCP option data. Not present if OpCode is 0 or 255 or if Length is zero. 37 | /// 38 | UINT8 Data[1]; 39 | } EFI_DHCP4_PACKET_OPTION; 40 | #pragma pack() 41 | 42 | 43 | #pragma pack(1) 44 | /// 45 | /// EFI_DHCP4_PACKET defines the format of DHCPv4 packets. See RFC 2131 for more information. 46 | /// 47 | typedef struct { 48 | UINT8 OpCode; 49 | UINT8 HwType; 50 | UINT8 HwAddrLen; 51 | UINT8 Hops; 52 | UINT32 Xid; 53 | UINT16 Seconds; 54 | UINT16 Reserved; 55 | EFI_IPv4_ADDRESS ClientAddr; ///< Client IP address from client. 56 | EFI_IPv4_ADDRESS YourAddr; ///< Client IP address from server. 57 | EFI_IPv4_ADDRESS ServerAddr; ///< IP address of next server in bootstrap. 58 | EFI_IPv4_ADDRESS GatewayAddr; ///< Relay agent IP address. 59 | UINT8 ClientHwAddr[16]; ///< Client hardware address. 60 | CHAR8 ServerName[64]; 61 | CHAR8 BootFileName[128]; 62 | }EFI_DHCP4_HEADER; 63 | #pragma pack() 64 | 65 | 66 | #pragma pack(1) 67 | typedef struct { 68 | /// 69 | /// Size of the EFI_DHCP4_PACKET buffer. 70 | /// 71 | UINT32 Size; 72 | /// 73 | /// Length of the EFI_DHCP4_PACKET from the first byte of the Header field 74 | /// to the last byte of the Option[] field. 75 | /// 76 | UINT32 Length; 77 | 78 | struct { 79 | /// 80 | /// DHCP packet header. 81 | /// 82 | EFI_DHCP4_HEADER Header; 83 | /// 84 | /// DHCP magik cookie in network byte order. 85 | /// 86 | UINT32 Magik; 87 | /// 88 | /// Start of the DHCP packed option data. 89 | /// 90 | UINT8 Option[1]; 91 | } Dhcp4; 92 | } EFI_DHCP4_PACKET; 93 | #pragma pack() 94 | 95 | 96 | typedef enum { 97 | /// 98 | /// The EFI DHCPv4 Protocol driver is stopped. 99 | /// 100 | Dhcp4Stopped = 0x0, 101 | /// 102 | /// The EFI DHCPv4 Protocol driver is inactive. 103 | /// 104 | Dhcp4Init = 0x1, 105 | /// 106 | /// The EFI DHCPv4 Protocol driver is collecting DHCP offer packets from DHCP servers. 107 | /// 108 | Dhcp4Selecting = 0x2, 109 | /// 110 | /// The EFI DHCPv4 Protocol driver has sent the request to the DHCP server and is waiting for a response. 111 | /// 112 | Dhcp4Requesting = 0x3, 113 | /// 114 | /// The DHCP configuration has completed. 115 | /// 116 | Dhcp4Bound = 0x4, 117 | /// 118 | /// The DHCP configuration is being renewed and another request has 119 | /// been sent out, but it has not received a response from the server yet. 120 | /// 121 | Dhcp4Renewing = 0x5, 122 | /// 123 | /// The DHCP configuration has timed out and the EFI DHCPv4 124 | /// Protocol driver is trying to extend the lease time. 125 | /// 126 | Dhcp4Rebinding = 0x6, 127 | /// 128 | /// The EFI DHCPv4 Protocol driver was initialized with a previously 129 | /// allocated or known IP address. 130 | /// 131 | Dhcp4InitReboot = 0x7, 132 | /// 133 | /// The EFI DHCPv4 Protocol driver is seeking to reuse the previously 134 | /// allocated IP address by sending a request to the DHCP server. 135 | /// 136 | Dhcp4Rebooting = 0x8 137 | } EFI_DHCP4_STATE; 138 | 139 | 140 | typedef enum{ 141 | /// 142 | /// The packet to start the configuration sequence is about to be sent. 143 | /// 144 | Dhcp4SendDiscover = 0x01, 145 | /// 146 | /// A reply packet was just received. 147 | /// 148 | Dhcp4RcvdOffer = 0x02, 149 | /// 150 | /// It is time for Dhcp4Callback to select an offer. 151 | /// 152 | Dhcp4SelectOffer = 0x03, 153 | /// 154 | /// A request packet is about to be sent. 155 | /// 156 | Dhcp4SendRequest = 0x04, 157 | /// 158 | /// A DHCPACK packet was received and will be passed to Dhcp4Callback. 159 | /// 160 | Dhcp4RcvdAck = 0x05, 161 | /// 162 | /// A DHCPNAK packet was received and will be passed to Dhcp4Callback. 163 | /// 164 | Dhcp4RcvdNak = 0x06, 165 | /// 166 | /// A decline packet is about to be sent. 167 | /// 168 | Dhcp4SendDecline = 0x07, 169 | /// 170 | /// The DHCP configuration process has completed. No packet is associated with this event. 171 | /// 172 | Dhcp4BoundCompleted = 0x08, 173 | /// 174 | /// It is time to enter the Dhcp4Renewing state and to contact the server 175 | /// that originally issued the network address. No packet is associated with this event. 176 | /// 177 | Dhcp4EnterRenewing = 0x09, 178 | /// 179 | /// It is time to enter the Dhcp4Rebinding state and to contact any server. 180 | /// No packet is associated with this event. 181 | /// 182 | Dhcp4EnterRebinding = 0x0a, 183 | /// 184 | /// The configured IP address was lost either because the lease has expired, 185 | /// the user released the configuration, or a DHCPNAK packet was received in 186 | /// the Dhcp4Renewing or Dhcp4Rebinding state. No packet is associated with this event. 187 | /// 188 | Dhcp4AddressLost = 0x0b, 189 | /// 190 | /// The DHCP process failed because a DHCPNAK packet was received or the user 191 | /// aborted the DHCP process at a time when the configuration was not available yet. 192 | /// No packet is associated with this event. 193 | /// 194 | Dhcp4Fail = 0x0c 195 | } EFI_DHCP4_EVENT; 196 | 197 | /** 198 | Callback routine. 199 | 200 | EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver 201 | to intercept events that occurred in the configuration process. This structure 202 | provides advanced control of each state transition of the DHCP process. The 203 | returned status code determines the behavior of the EFI DHCPv4 Protocol driver. 204 | There are three possible returned values, which are described in the following 205 | table. 206 | 207 | @param This The pointer to the EFI DHCPv4 Protocol instance that is used to 208 | configure this callback function. 209 | @param Context The pointer to the context that is initialized by 210 | EFI_DHCP4_PROTOCOL.Configure(). 211 | @param CurrentState The current operational state of the EFI DHCPv4 Protocol 212 | driver. 213 | @param Dhcp4Event The event that occurs in the current state, which usually means a 214 | state transition. 215 | @param Packet The DHCP packet that is going to be sent or already received. 216 | @param NewPacket The packet that is used to replace the above Packet. 217 | 218 | @retval EFI_SUCCESS Tells the EFI DHCPv4 Protocol driver to continue the DHCP process. 219 | When it is in the Dhcp4Selecting state, it tells the EFI DHCPv4 Protocol 220 | driver to stop collecting additional packets. The driver will exit 221 | the Dhcp4Selecting state and enter the Dhcp4Requesting state. 222 | @retval EFI_NOT_READY Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol 223 | driver will continue to wait for more packets until the retry 224 | timeout expires. 225 | @retval EFI_ABORTED Tells the EFI DHCPv4 Protocol driver to abort the current process and 226 | return to the Dhcp4Init or Dhcp4InitReboot state. 227 | 228 | **/ 229 | typedef 230 | EFI_STATUS 231 | (EFIAPI *EFI_DHCP4_CALLBACK)( 232 | IN EFI_DHCP4_PROTOCOL *This, 233 | IN VOID *Context, 234 | IN EFI_DHCP4_STATE CurrentState, 235 | IN EFI_DHCP4_EVENT Dhcp4Event, 236 | IN EFI_DHCP4_PACKET *Packet OPTIONAL, 237 | OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL 238 | ); 239 | 240 | typedef struct { 241 | /// 242 | /// The number of times to try sending a packet during the Dhcp4SendDiscover 243 | /// event and waiting for a response during the Dhcp4RcvdOffer event. 244 | /// Set to zero to use the default try counts and timeout values. 245 | /// 246 | UINT32 DiscoverTryCount; 247 | /// 248 | /// The maximum amount of time (in seconds) to wait for returned packets in each 249 | /// of the retries. Timeout values of zero will default to a timeout value 250 | /// of one second. Set to NULL to use default timeout values. 251 | /// 252 | UINT32 *DiscoverTimeout; 253 | /// 254 | /// The number of times to try sending a packet during the Dhcp4SendRequest event 255 | /// and waiting for a response during the Dhcp4RcvdAck event before accepting 256 | /// failure. Set to zero to use the default try counts and timeout values. 257 | /// 258 | UINT32 RequestTryCount; 259 | /// 260 | /// The maximum amount of time (in seconds) to wait for return packets in each of the retries. 261 | /// Timeout values of zero will default to a timeout value of one second. 262 | /// Set to NULL to use default timeout values. 263 | /// 264 | UINT32 *RequestTimeout; 265 | /// 266 | /// For a DHCPDISCOVER, setting this parameter to the previously allocated IP 267 | /// address will cause the EFI DHCPv4 Protocol driver to enter the Dhcp4InitReboot state. 268 | /// And set this field to 0.0.0.0 to enter the Dhcp4Init state. 269 | /// For a DHCPINFORM this parameter should be set to the client network address 270 | /// which was assigned to the client during a DHCPDISCOVER. 271 | /// 272 | EFI_IPv4_ADDRESS ClientAddress; 273 | /// 274 | /// The callback function to intercept various events that occurred in 275 | /// the DHCP configuration process. Set to NULL to ignore all those events. 276 | /// 277 | EFI_DHCP4_CALLBACK Dhcp4Callback; 278 | /// 279 | /// The pointer to the context that will be passed to Dhcp4Callback when it is called. 280 | /// 281 | VOID *CallbackContext; 282 | /// 283 | /// Number of DHCP options in the OptionList. 284 | /// 285 | UINT32 OptionCount; 286 | /// 287 | /// List of DHCP options to be included in every packet that is sent during the 288 | /// Dhcp4SendDiscover event. Pad options are appended automatically by DHCP driver 289 | /// in outgoing DHCP packets. If OptionList itself contains pad option, they are 290 | /// ignored by the driver. OptionList can be freed after EFI_DHCP4_PROTOCOL.Configure() 291 | /// returns. Ignored if OptionCount is zero. 292 | /// 293 | EFI_DHCP4_PACKET_OPTION **OptionList; 294 | } EFI_DHCP4_CONFIG_DATA; 295 | 296 | 297 | typedef struct { 298 | /// 299 | /// The EFI DHCPv4 Protocol driver operating state. 300 | /// 301 | EFI_DHCP4_STATE State; 302 | /// 303 | /// The configuration data of the current EFI DHCPv4 Protocol driver instance. 304 | /// 305 | EFI_DHCP4_CONFIG_DATA ConfigData; 306 | /// 307 | /// The client IP address that was acquired from the DHCP server. If it is zero, 308 | /// the DHCP acquisition has not completed yet and the following fields in this structure are undefined. 309 | /// 310 | EFI_IPv4_ADDRESS ClientAddress; 311 | /// 312 | /// The local hardware address. 313 | /// 314 | EFI_MAC_ADDRESS ClientMacAddress; 315 | /// 316 | /// The server IP address that is providing the DHCP service to this client. 317 | /// 318 | EFI_IPv4_ADDRESS ServerAddress; 319 | /// 320 | /// The router IP address that was acquired from the DHCP server. 321 | /// May be zero if the server does not offer this address. 322 | /// 323 | EFI_IPv4_ADDRESS RouterAddress; 324 | /// 325 | /// The subnet mask of the connected network that was acquired from the DHCP server. 326 | /// 327 | EFI_IPv4_ADDRESS SubnetMask; 328 | /// 329 | /// The lease time (in 1-second units) of the configured IP address. 330 | /// The value 0xFFFFFFFF means that the lease time is infinite. 331 | /// A default lease of 7 days is used if the DHCP server does not provide a value. 332 | /// 333 | UINT32 LeaseTime; 334 | /// 335 | /// The cached latest DHCPACK or DHCPNAK or BOOTP REPLY packet. May be NULL if no packet is cached. 336 | /// 337 | EFI_DHCP4_PACKET *ReplyPacket; 338 | } EFI_DHCP4_MODE_DATA; 339 | 340 | 341 | typedef struct { 342 | /// 343 | /// Alternate listening address. It can be a unicast, multicast, or broadcast address. 344 | /// 345 | EFI_IPv4_ADDRESS ListenAddress; 346 | /// 347 | /// The subnet mask of above listening unicast/broadcast IP address. 348 | /// Ignored if ListenAddress is a multicast address. 349 | /// 350 | EFI_IPv4_ADDRESS SubnetMask; 351 | /// 352 | /// Alternate station source (or listening) port number. 353 | /// If zero, then the default station port number (68) will be used. 354 | /// 355 | UINT16 ListenPort; 356 | } EFI_DHCP4_LISTEN_POINT; 357 | 358 | 359 | typedef struct { 360 | /// 361 | /// The completion status of transmitting and receiving. 362 | /// 363 | EFI_STATUS Status; 364 | /// 365 | /// If not NULL, the event that will be signaled when the collection process 366 | /// completes. If NULL, this function will busy-wait until the collection process competes. 367 | /// 368 | EFI_EVENT CompletionEvent; 369 | /// 370 | /// The pointer to the server IP address. This address may be a unicast, multicast, or broadcast address. 371 | /// 372 | EFI_IPv4_ADDRESS RemoteAddress; 373 | /// 374 | /// The server listening port number. If zero, the default server listening port number (67) will be used. 375 | /// 376 | UINT16 RemotePort; 377 | /// 378 | /// The pointer to the gateway address to override the existing setting. 379 | /// 380 | EFI_IPv4_ADDRESS GatewayAddress; 381 | /// 382 | /// The number of entries in ListenPoints. If zero, the default station address and port number 68 are used. 383 | /// 384 | UINT32 ListenPointCount; 385 | /// 386 | /// An array of station address and port number pairs that are used as receiving filters. 387 | /// The first entry is also used as the source address and source port of the outgoing packet. 388 | /// 389 | EFI_DHCP4_LISTEN_POINT *ListenPoints; 390 | /// 391 | /// The number of seconds to collect responses. Zero is invalid. 392 | /// 393 | UINT32 TimeoutValue; 394 | /// 395 | /// The pointer to the packet to be transmitted. 396 | /// 397 | EFI_DHCP4_PACKET *Packet; 398 | /// 399 | /// Number of received packets. 400 | /// 401 | UINT32 ResponseCount; 402 | /// 403 | /// The pointer to the allocated list of received packets. 404 | /// 405 | EFI_DHCP4_PACKET *ResponseList; 406 | } EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN; 407 | 408 | 409 | /** 410 | Returns the current operating mode and cached data packet for the EFI DHCPv4 Protocol driver. 411 | 412 | The GetModeData() function returns the current operating mode and cached data 413 | packet for the EFI DHCPv4 Protocol driver. 414 | 415 | @param This The pointer to the EFI_DHCP4_PROTOCOL instance. 416 | @param Dhcp4ModeData The pointer to storage for the EFI_DHCP4_MODE_DATA structure. 417 | 418 | @retval EFI_SUCCESS The mode data was returned. 419 | @retval EFI_INVALID_PARAMETER This is NULL. 420 | 421 | **/ 422 | typedef 423 | EFI_STATUS 424 | (EFIAPI *EFI_DHCP4_GET_MODE_DATA)( 425 | IN EFI_DHCP4_PROTOCOL *This, 426 | OUT EFI_DHCP4_MODE_DATA *Dhcp4ModeData 427 | ); 428 | 429 | /** 430 | Initializes, changes, or resets the operational settings for the EFI DHCPv4 Protocol driver. 431 | 432 | The Configure() function is used to initialize, change, or reset the operational 433 | settings of the EFI DHCPv4 Protocol driver for the communication device on which 434 | the EFI DHCPv4 Service Binding Protocol is installed. This function can be 435 | successfully called only if both of the following are true: 436 | * This instance of the EFI DHCPv4 Protocol driver is in the Dhcp4Stopped, Dhcp4Init, 437 | Dhcp4InitReboot, or Dhcp4Bound states. 438 | * No other EFI DHCPv4 Protocol driver instance that is controlled by this EFI 439 | DHCPv4 Service Binding Protocol driver instance has configured this EFI DHCPv4 440 | Protocol driver. 441 | When this driver is in the Dhcp4Stopped state, it can transfer into one of the 442 | following two possible initial states: 443 | * Dhcp4Init 444 | * Dhcp4InitReboot. 445 | The driver can transfer into these states by calling Configure() with a non-NULL 446 | Dhcp4CfgData. The driver will transfer into the appropriate state based on the 447 | supplied client network address in the ClientAddress parameter and DHCP options 448 | in the OptionList parameter as described in RFC 2131. 449 | When Configure() is called successfully while Dhcp4CfgData is set to NULL, the 450 | default configuring data will be reset in the EFI DHCPv4 Protocol driver and 451 | the state of the EFI DHCPv4 Protocol driver will not be changed. If one instance 452 | wants to make it possible for another instance to configure the EFI DHCPv4 Protocol 453 | driver, it must call this function with Dhcp4CfgData set to NULL. 454 | 455 | @param This The pointer to the EFI_DHCP4_PROTOCOL instance. 456 | @param Dhcp4CfgData The pointer to the EFI_DHCP4_CONFIG_DATA. 457 | 458 | @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Init or 459 | Dhcp4InitReboot state, if the original state of this driver 460 | was Dhcp4Stopped, Dhcp4Init,Dhcp4InitReboot, or Dhcp4Bound 461 | and the value of Dhcp4CfgData was not NULL. 462 | Otherwise, the state was left unchanged. 463 | @retval EFI_ACCESS_DENIED This instance of the EFI DHCPv4 Protocol driver was not in the 464 | Dhcp4Stopped, Dhcp4Init, Dhcp4InitReboot, or Dhcp4Bound state; 465 | Or onother instance of this EFI DHCPv4 Protocol driver is already 466 | in a valid configured state. 467 | @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE: 468 | This is NULL. 469 | DiscoverTryCount > 0 and DiscoverTimeout is NULL 470 | RequestTryCount > 0 and RequestTimeout is NULL. 471 | OptionCount >0 and OptionList is NULL. 472 | ClientAddress is not a valid unicast address. 473 | @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. 474 | @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. 475 | 476 | **/ 477 | typedef 478 | EFI_STATUS 479 | (EFIAPI *EFI_DHCP4_CONFIGURE)( 480 | IN EFI_DHCP4_PROTOCOL *This, 481 | IN EFI_DHCP4_CONFIG_DATA *Dhcp4CfgData OPTIONAL 482 | ); 483 | 484 | 485 | /** 486 | Starts the DHCP configuration process. 487 | 488 | The Start() function starts the DHCP configuration process. This function can 489 | be called only when the EFI DHCPv4 Protocol driver is in the Dhcp4Init or 490 | Dhcp4InitReboot state. 491 | If the DHCP process completes successfully, the state of the EFI DHCPv4 Protocol 492 | driver will be transferred through Dhcp4Selecting and Dhcp4Requesting to the 493 | Dhcp4Bound state. The CompletionEvent will then be signaled if it is not NULL. 494 | If the process aborts, either by the user or by some unexpected network error, 495 | the state is restored to the Dhcp4Init state. The Start() function can be called 496 | again to restart the process. 497 | Refer to RFC 2131 for precise state transitions during this process. At the 498 | time when each event occurs in this process, the callback function that was set 499 | by EFI_DHCP4_PROTOCOL.Configure() will be called and the user can take this 500 | opportunity to control the process. 501 | 502 | @param This The pointer to the EFI_DHCP4_PROTOCOL instance. 503 | @param CompletionEvent If not NULL, it indicates the event that will be signaled when the 504 | EFI DHCPv4 Protocol driver is transferred into the 505 | Dhcp4Bound state or when the DHCP process is aborted. 506 | EFI_DHCP4_PROTOCOL.GetModeData() can be called to 507 | check the completion status. If NULL, 508 | EFI_DHCP4_PROTOCOL.Start() will wait until the driver 509 | is transferred into the Dhcp4Bound state or the process fails. 510 | 511 | @retval EFI_SUCCESS The DHCP configuration process has started, or it has completed 512 | when CompletionEvent is NULL. 513 | @retval EFI_NOT_STARTED The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped 514 | state. EFI_DHCP4_PROTOCOL. Configure() needs to be called. 515 | @retval EFI_INVALID_PARAMETER This is NULL. 516 | @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. 517 | @retval EFI_TIMEOUT The DHCP configuration process failed because no response was 518 | received from the server within the specified timeout value. 519 | @retval EFI_ABORTED The user aborted the DHCP process. 520 | @retval EFI_ALREADY_STARTED Some other EFI DHCPv4 Protocol instance already started the 521 | DHCP process. 522 | @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. 523 | @retval EFI_NO_MEDIA There was a media error. 524 | 525 | **/ 526 | typedef 527 | EFI_STATUS 528 | (EFIAPI *EFI_DHCP4_START)( 529 | IN EFI_DHCP4_PROTOCOL *This, 530 | IN EFI_EVENT CompletionEvent OPTIONAL 531 | ); 532 | 533 | /** 534 | Extends the lease time by sending a request packet. 535 | 536 | The RenewRebind() function is used to manually extend the lease time when the 537 | EFI DHCPv4 Protocol driver is in the Dhcp4Bound state, and the lease time has 538 | not expired yet. This function will send a request packet to the previously 539 | found server (or to any server when RebindRequest is TRUE) and transfer the 540 | state into the Dhcp4Renewing state (or Dhcp4Rebinding when RebindingRequest is 541 | TRUE). When a response is received, the state is returned to Dhcp4Bound. 542 | If no response is received before the try count is exceeded (the RequestTryCount 543 | field that is specified in EFI_DHCP4_CONFIG_DATA) but before the lease time that 544 | was issued by the previous server expires, the driver will return to the Dhcp4Bound 545 | state, and the previous configuration is restored. The outgoing and incoming packets 546 | can be captured by the EFI_DHCP4_CALLBACK function. 547 | 548 | @param This The pointer to the EFI_DHCP4_PROTOCOL instance. 549 | @param RebindRequest If TRUE, this function broadcasts the request packets and enters 550 | the Dhcp4Rebinding state. Otherwise, it sends a unicast 551 | request packet and enters the Dhcp4Renewing state. 552 | @param CompletionEvent If not NULL, this event is signaled when the renew/rebind phase 553 | completes or some error occurs. 554 | EFI_DHCP4_PROTOCOL.GetModeData() can be called to 555 | check the completion status. If NULL, 556 | EFI_DHCP4_PROTOCOL.RenewRebind() will busy-wait 557 | until the DHCP process finishes. 558 | 559 | @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the 560 | Dhcp4Renewing state or is back to the Dhcp4Bound state. 561 | @retval EFI_NOT_STARTED The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped 562 | state. EFI_DHCP4_PROTOCOL.Configure() needs to 563 | be called. 564 | @retval EFI_INVALID_PARAMETER This is NULL. 565 | @retval EFI_TIMEOUT There was no response from the server when the try count was 566 | exceeded. 567 | @retval EFI_ACCESS_DENIED The driver is not in the Dhcp4Bound state. 568 | @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. 569 | 570 | **/ 571 | typedef 572 | EFI_STATUS 573 | (EFIAPI *EFI_DHCP4_RENEW_REBIND)( 574 | IN EFI_DHCP4_PROTOCOL *This, 575 | IN BOOLEAN RebindRequest, 576 | IN EFI_EVENT CompletionEvent OPTIONAL 577 | ); 578 | 579 | /** 580 | Releases the current address configuration. 581 | 582 | The Release() function releases the current configured IP address by doing either 583 | of the following: 584 | * Sending a DHCPRELEASE packet when the EFI DHCPv4 Protocol driver is in the 585 | Dhcp4Bound state 586 | * Setting the previously assigned IP address that was provided with the 587 | EFI_DHCP4_PROTOCOL.Configure() function to 0.0.0.0 when the driver is in 588 | Dhcp4InitReboot state 589 | After a successful call to this function, the EFI DHCPv4 Protocol driver returns 590 | to the Dhcp4Init state, and any subsequent incoming packets will be discarded silently. 591 | 592 | @param This The pointer to the EFI_DHCP4_PROTOCOL instance. 593 | 594 | @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Init phase. 595 | @retval EFI_INVALID_PARAMETER This is NULL. 596 | @retval EFI_ACCESS_DENIED The EFI DHCPv4 Protocol driver is not Dhcp4InitReboot state. 597 | @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. 598 | 599 | **/ 600 | typedef 601 | EFI_STATUS 602 | (EFIAPI *EFI_DHCP4_RELEASE)( 603 | IN EFI_DHCP4_PROTOCOL *This 604 | ); 605 | 606 | /** 607 | Stops the current address configuration. 608 | 609 | The Stop() function is used to stop the DHCP configuration process. After this 610 | function is called successfully, the EFI DHCPv4 Protocol driver is transferred 611 | into the Dhcp4Stopped state. EFI_DHCP4_PROTOCOL.Configure() needs to be called 612 | before DHCP configuration process can be started again. This function can be 613 | called when the EFI DHCPv4 Protocol driver is in any state. 614 | 615 | @param This The pointer to the EFI_DHCP4_PROTOCOL instance. 616 | 617 | @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Stopped phase. 618 | @retval EFI_INVALID_PARAMETER This is NULL. 619 | 620 | **/ 621 | typedef 622 | EFI_STATUS 623 | (EFIAPI *EFI_DHCP4_STOP)( 624 | IN EFI_DHCP4_PROTOCOL *This 625 | ); 626 | 627 | /** 628 | Builds a DHCP packet, given the options to be appended or deleted or replaced. 629 | 630 | The Build() function is used to assemble a new packet from the original packet 631 | by replacing or deleting existing options or appending new options. This function 632 | does not change any state of the EFI DHCPv4 Protocol driver and can be used at 633 | any time. 634 | 635 | @param This The pointer to the EFI_DHCP4_PROTOCOL instance. 636 | @param SeedPacket Initial packet to be used as a base for building new packet. 637 | @param DeleteCount Number of opcodes in the DeleteList. 638 | @param DeleteList List of opcodes to be deleted from the seed packet. 639 | Ignored if DeleteCount is zero. 640 | @param AppendCount Number of entries in the OptionList. 641 | @param AppendList The pointer to a DHCP option list to be appended to SeedPacket. 642 | If SeedPacket also contains options in this list, they are 643 | replaced by new options (except pad option). Ignored if 644 | AppendCount is zero. Type EFI_DHCP4_PACKET_OPTION 645 | @param NewPacket The pointer to storage for the pointer to the new allocated packet. 646 | Use the EFI Boot Service FreePool() on the resulting pointer 647 | when done with the packet. 648 | 649 | @retval EFI_SUCCESS The new packet was built. 650 | @retval EFI_OUT_OF_RESOURCES Storage for the new packet could not be allocated. 651 | @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: 652 | This is NULL. 653 | SeedPacket is NULL. 654 | SeedPacket is not a well-formed DHCP packet. 655 | AppendCount is not zero and AppendList is NULL. 656 | DeleteCount is not zero and DeleteList is NULL. 657 | NewPacket is NULL 658 | Both DeleteCount and AppendCount are zero and 659 | NewPacket is not NULL. 660 | 661 | **/ 662 | typedef 663 | EFI_STATUS 664 | (EFIAPI *EFI_DHCP4_BUILD)( 665 | IN EFI_DHCP4_PROTOCOL *This, 666 | IN EFI_DHCP4_PACKET *SeedPacket, 667 | IN UINT32 DeleteCount, 668 | IN UINT8 *DeleteList OPTIONAL, 669 | IN UINT32 AppendCount, 670 | IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL, 671 | OUT EFI_DHCP4_PACKET **NewPacket 672 | ); 673 | 674 | 675 | /** 676 | Transmits a DHCP formatted packet and optionally waits for responses. 677 | 678 | The TransmitReceive() function is used to transmit a DHCP packet and optionally 679 | wait for the response from servers. This function does not change the state of 680 | the EFI DHCPv4 Protocol driver. It can be used at any time because of this. 681 | 682 | @param This The pointer to the EFI_DHCP4_PROTOCOL instance. 683 | @param Token The pointer to the EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN structure. 684 | 685 | @retval EFI_SUCCESS The packet was successfully queued for transmission. 686 | @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: 687 | This is NULL. 688 | Token.RemoteAddress is zero. 689 | Token.Packet is NULL. 690 | Token.Packet is not a well-formed DHCP packet. 691 | The transaction ID in Token.Packet is in use by another DHCP process. 692 | @retval EFI_NOT_READY The previous call to this function has not finished yet. Try to call 693 | this function after collection process completes. 694 | @retval EFI_NO_MAPPING The default station address is not available yet. 695 | @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. 696 | @retval EFI_UNSUPPORTED The implementation doesn't support this function 697 | @retval Others Some other unexpected error occurred. 698 | 699 | **/ 700 | typedef 701 | EFI_STATUS 702 | (EFIAPI *EFI_DHCP4_TRANSMIT_RECEIVE)( 703 | IN EFI_DHCP4_PROTOCOL *This, 704 | IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token 705 | ); 706 | 707 | 708 | /** 709 | Parses the packed DHCP option data. 710 | 711 | The Parse() function is used to retrieve the option list from a DHCP packet. 712 | If *OptionCount isn't zero, and there is enough space for all the DHCP options 713 | in the Packet, each element of PacketOptionList is set to point to somewhere in 714 | the Packet->Dhcp4.Option where a new DHCP option begins. If RFC3396 is supported, 715 | the caller should reassemble the parsed DHCP options to get the final result. 716 | If *OptionCount is zero or there isn't enough space for all of them, the number 717 | of DHCP options in the Packet is returned in OptionCount. 718 | 719 | @param This The pointer to the EFI_DHCP4_PROTOCOL instance. 720 | @param Packet The pointer to packet to be parsed. 721 | @param OptionCount On input, the number of entries in the PacketOptionList. 722 | On output, the number of entries that were written into the 723 | PacketOptionList. 724 | @param PacketOptionList A list of packet option entries to be filled in. End option or pad 725 | options are not included. 726 | 727 | @retval EFI_SUCCESS The packet was successfully parsed. 728 | @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: 729 | This is NULL. 730 | The packet is NULL. 731 | The packet is not a well-formed DHCP packet. 732 | OptionCount is NULL. 733 | @retval EFI_BUFFER_TOO_SMALL One or more of the following conditions is TRUE: 734 | 1) *OptionCount is smaller than the number of options that 735 | were found in the Packet. 736 | 2) PacketOptionList is NULL. 737 | @retval EFI_OUT_OF_RESOURCE The packet failed to parse because of a resource shortage. 738 | 739 | **/ 740 | typedef 741 | EFI_STATUS 742 | (EFIAPI *EFI_DHCP4_PARSE)( 743 | IN EFI_DHCP4_PROTOCOL *This, 744 | IN EFI_DHCP4_PACKET *Packet, 745 | IN OUT UINT32 *OptionCount, 746 | OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL 747 | ); 748 | 749 | /// 750 | /// This protocol is used to collect configuration information for the EFI IPv4 Protocol drivers 751 | /// and to provide DHCPv4 server and PXE boot server discovery services. 752 | /// 753 | struct _EFI_DHCP4_PROTOCOL { 754 | EFI_DHCP4_GET_MODE_DATA GetModeData; 755 | EFI_DHCP4_CONFIGURE Configure; 756 | EFI_DHCP4_START Start; 757 | EFI_DHCP4_RENEW_REBIND RenewRebind; 758 | EFI_DHCP4_RELEASE Release; 759 | EFI_DHCP4_STOP Stop; 760 | EFI_DHCP4_BUILD Build; 761 | EFI_DHCP4_TRANSMIT_RECEIVE TransmitReceive; 762 | EFI_DHCP4_PARSE Parse; 763 | }; 764 | 765 | extern EFI_GUID gEfiDhcp4ProtocolGuid; 766 | extern EFI_GUID gEfiDhcp4ServiceBindingProtocolGuid; 767 | 768 | #endif 769 | -------------------------------------------------------------------------------- /module/efinet.c: -------------------------------------------------------------------------------- 1 | /* UEFI Network interface. 2 | * 3 | * This implements the simplest possible polled network interface 4 | * ontop of the EFI NIC protocol. 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "efiwrapper.h" 14 | #include "efinet.h" 15 | #include "efidhcp4.h" 16 | 17 | typedef struct { 18 | spinlock_t lock; 19 | EFI_SIMPLE_NETWORK_PROTOCOL * uefi_nic; 20 | EFI_HANDLE uefi_handle; 21 | int id; 22 | int up; 23 | struct net_device * dev; 24 | struct net_device_stats stats; 25 | struct sk_buff * rx_skb; 26 | } uefi_nic_t; 27 | 28 | #define MAX_NICS 16 29 | static struct timer_list uefi_rx_timer; 30 | static uefi_nic_t * uefi_nics[MAX_NICS]; 31 | static int uefi_nic_count; 32 | 33 | static int uefi_net_rx(uefi_nic_t * nic) 34 | { 35 | struct sk_buff * skb; 36 | UINTN pkt_len = 1500; 37 | unsigned long flags; 38 | int status; 39 | 40 | // if we are no longer up, stop scheduling 41 | if (!nic->up) 42 | return 0; 43 | 44 | if (nic->rx_skb == NULL) 45 | { 46 | // replenish our rx packet and align it 47 | nic->rx_skb = netdev_alloc_skb(nic->dev, pkt_len + 5); 48 | skb_reserve(nic->rx_skb, NET_IP_ALIGN); 49 | } 50 | 51 | spin_lock_irqsave(&nic->lock, flags); 52 | skb = nic->rx_skb; 53 | 54 | status = nic->uefi_nic->Receive( 55 | nic->uefi_nic, 56 | NULL, // header size, no processing required 57 | &pkt_len, 58 | skb_tail_pointer(skb), 59 | NULL, // src addr 60 | NULL, // dst addr, 61 | NULL // proto 62 | ); 63 | 64 | // if we have successful in receiving the packet 65 | // remove this skb from the rx pool 66 | if (status == 0) 67 | nic->rx_skb = NULL; 68 | 69 | spin_unlock_irqrestore(&nic->lock, flags); 70 | 71 | if (status == 6) // EFI_NOT_READY) 72 | { 73 | // no packet to receive 74 | } else 75 | if (status == 0) 76 | { 77 | // success! update the actual length received 78 | // which has already been copied into the buffer 79 | skb_put(skb, pkt_len); 80 | skb->protocol = eth_type_trans(skb, nic->dev); 81 | //printk("uefi%d: rx %lld bytes proto %d\n", nic->id, pkt_len, skb->protocol); 82 | netif_rx(skb); 83 | } else { 84 | printk("uefi%d: error %d\n", nic->id, status); 85 | } 86 | 87 | return 1; 88 | } 89 | 90 | static void uefi_net_poll(struct timer_list * timer) 91 | { 92 | uefi_memory_map_add(); 93 | 94 | // try to clear the queues on the NICs 95 | for(int loops = 0 ; loops < 10 ; loops++) 96 | { 97 | int count = 0; 98 | for(int i = 0 ; i < uefi_nic_count ; i++) 99 | count += uefi_net_rx(uefi_nics[i]); 100 | if (count == 0) 101 | break; 102 | } 103 | 104 | // reschedule our selves to check again 105 | mod_timer(timer, jiffies + msecs_to_jiffies(5)); 106 | } 107 | 108 | 109 | static int uefi_net_open(struct net_device * dev) 110 | { 111 | uefi_nic_t * nic = netdev_priv(dev); 112 | int status; 113 | 114 | uefi_memory_map_add(); 115 | 116 | status = nic->uefi_nic->Start(nic->uefi_nic); 117 | 118 | // 0 == success, 20 == already started 119 | if (status != 0 && status != 20) 120 | { 121 | printk("uefi%d: start returned %d\n", nic->id, status); 122 | return -1; 123 | } 124 | 125 | printk("uefi%d: started nic\n", nic->id); 126 | nic->up = 1; 127 | 128 | return 0; 129 | } 130 | 131 | static int uefi_net_stop(struct net_device * dev) 132 | { 133 | uefi_nic_t * nic = netdev_priv(dev); 134 | int status; 135 | 136 | uefi_memory_map_add(); 137 | 138 | nic->up = 0; // we'll stop scheduling timers 139 | 140 | status = nic->uefi_nic->Shutdown(nic->uefi_nic); 141 | if (status != 0) 142 | { 143 | printk("uefi%d: stop returned %d\n", nic->id, status); 144 | return -1; 145 | } 146 | 147 | printk("uefi%d: stopped nic\n", nic->id); 148 | return 0; 149 | } 150 | 151 | static netdev_tx_t uefi_net_xmit(struct sk_buff * skb, struct net_device * dev) 152 | { 153 | uefi_nic_t * nic = netdev_priv(dev); 154 | unsigned long flags; 155 | int status; 156 | 157 | uefi_memory_map_add(); 158 | 159 | spin_lock_irqsave(&nic->lock, flags); 160 | 161 | status = nic->uefi_nic->Transmit( 162 | nic->uefi_nic, 163 | 0, // HeaderSize 0 == packet is fully formed 164 | skb->len, // BufferSize 165 | skb->data, // Buffer, 166 | NULL, // SrcAddr, unused since HeaderSize == 0 167 | NULL, // DstAddr, unused 168 | NULL // Protocol, unused 169 | ); 170 | 171 | spin_unlock_irqrestore(&nic->lock, flags); 172 | 173 | dev_consume_skb_any(skb); 174 | 175 | if (status != 0) 176 | { 177 | // should return NETDEV_TX_BUSY, but :shrug:? 178 | if (status == 9) 179 | return NETDEV_TX_OK; 180 | 181 | printk("uefi%d: tx failed %d\n", nic->id, status); 182 | return -1; 183 | } 184 | 185 | return NETDEV_TX_OK; 186 | } 187 | 188 | static struct net_device_stats * uefi_net_stats(struct net_device * dev) 189 | { 190 | uefi_nic_t * nic = netdev_priv(dev); 191 | struct net_device_stats * stats = &nic->stats; 192 | EFI_NETWORK_STATISTICS uefi_stats; 193 | UINTN uefi_stats_size = sizeof(uefi_stats); 194 | unsigned long flags; 195 | 196 | uefi_memory_map_add(); 197 | 198 | spin_lock_irqsave(&nic->lock, flags); 199 | 200 | nic->uefi_nic->Statistics( 201 | nic->uefi_nic, 202 | 0, // do not reset 203 | &uefi_stats_size, 204 | &uefi_stats 205 | ); 206 | 207 | spin_unlock_irqrestore(&nic->lock, flags); 208 | 209 | // translate the stats to Linux from UEFI 210 | stats->rx_bytes = uefi_stats.RxTotalBytes; 211 | stats->rx_packets = uefi_stats.RxTotalFrames; 212 | stats->rx_errors = uefi_stats.RxTotalFrames - uefi_stats.RxGoodFrames; 213 | stats->rx_dropped = uefi_stats.RxDroppedFrames; 214 | stats->rx_length_errors = uefi_stats.RxUndersizeFrames; 215 | stats->rx_over_errors = uefi_stats.RxOversizeFrames; 216 | stats->rx_crc_errors = uefi_stats.RxCrcErrorFrames; 217 | 218 | stats->tx_bytes = uefi_stats.TxTotalBytes; 219 | stats->tx_packets = uefi_stats.TxTotalFrames; 220 | stats->tx_errors = uefi_stats.TxTotalFrames - uefi_stats.RxGoodFrames; 221 | stats->tx_dropped = uefi_stats.TxDroppedFrames; 222 | 223 | stats->multicast = uefi_stats.RxMulticastFrames; 224 | stats->collisions = uefi_stats.Collisions; 225 | 226 | // there are other stats that could be copied? 227 | return stats; 228 | } 229 | 230 | 231 | static struct net_device_ops uefi_nic_ops = { 232 | .ndo_open = uefi_net_open, 233 | .ndo_stop = uefi_net_stop, 234 | .ndo_start_xmit = uefi_net_xmit, 235 | .ndo_get_stats = uefi_net_stats, 236 | //.ndo_poll_controller = uefi_net_poll, 237 | }; 238 | 239 | static void 240 | set_sockaddr(struct sockaddr_in *sin, __be32 addr, __be16 port) 241 | { 242 | sin->sin_family = AF_INET; 243 | sin->sin_addr.s_addr = addr; 244 | sin->sin_port = port; 245 | } 246 | 247 | static int 248 | uefi_nic_set_address( 249 | uefi_nic_t * uefi_nic, 250 | EFI_IPv4_ADDRESS * efi_myaddr, 251 | EFI_IPv4_ADDRESS * efi_netmask, 252 | EFI_IPv4_ADDRESS * efi_gateway 253 | ) 254 | { 255 | struct net_device * dev = uefi_nic->dev; 256 | 257 | // copied from net/ipv4/ipconfig.c ic_setup_if() and ic_setup_routes() 258 | struct rtentry rm; 259 | struct ifreq ir; 260 | struct sockaddr_in * sin = (void *) &ir.ifr_ifru.ifru_addr; 261 | int err; 262 | 263 | __be32 myaddr = *(__be32*) efi_myaddr->Addr; 264 | __be32 netmask = *(__be32*) efi_netmask->Addr; 265 | __be32 gateway = *(__be32*) efi_gateway->Addr; 266 | 267 | memset(&ir, 0, sizeof(ir)); 268 | strcpy(ir.ifr_ifrn.ifrn_name, dev->name); 269 | 270 | set_sockaddr(sin, myaddr, 0); 271 | if ((err = devinet_ioctl(&init_net, SIOCSIFADDR, &ir)) < 0) 272 | { 273 | pr_err("uefi-ip-config: unable to set interface address (%d)\n", err); 274 | return -1; 275 | } 276 | 277 | set_sockaddr(sin, netmask, 0); 278 | if ((err = devinet_ioctl(&init_net, SIOCSIFNETMASK, &ir)) < 0) 279 | { 280 | pr_err("uefi-ip-config: unable to set interface netmask (%d)\n", err); 281 | return -1; 282 | } 283 | 284 | set_sockaddr(sin, myaddr | ~netmask, 0); 285 | if ((err = devinet_ioctl(&init_net, SIOCSIFBRDADDR, &ir)) < 0) 286 | { 287 | pr_err("uefi-ip-config: unable to set interface broadcast (%d)\n", err); 288 | return -1; 289 | } 290 | 291 | // bring up the network before adding the route 292 | // this will call our init routine, which will schedule our timers 293 | if ((err = dev_change_flags(dev, dev->flags | IFF_UP, NULL)) < 0) 294 | { 295 | pr_err("uefi-ip-config: can not bring up network (%d)\n", err); 296 | return -1; 297 | } 298 | 299 | // configure the default route 300 | memset(&rm, 0, sizeof(rm)); 301 | set_sockaddr((struct sockaddr_in *) &rm.rt_dst, 0, 0); 302 | set_sockaddr((struct sockaddr_in *) &rm.rt_genmask, 0, 0); 303 | set_sockaddr((struct sockaddr_in *) &rm.rt_gateway, gateway, 0); 304 | rm.rt_flags = RTF_UP | RTF_GATEWAY; 305 | if ((err = ip_rt_ioctl(&init_net, SIOCADDRT, &rm)) < 0) { 306 | pr_err("uefi-ip-config: cannot add default route (%d)\n", err); 307 | return -1; 308 | } 309 | 310 | return 0; 311 | } 312 | 313 | static void uefi_nic_autoconfig(uefi_nic_t * nic) 314 | { 315 | EFI_DHCP4_PROTOCOL * dhcp4 = uefi_locate_and_handle_protocol(&EFI_DHCP4_PROTOCOL_GUID); 316 | EFI_DHCP4_MODE_DATA config; 317 | int status; 318 | 319 | if (!dhcp4) 320 | { 321 | printk("UEFI DHCP not supported?\n"); 322 | return; 323 | } 324 | 325 | status = dhcp4->GetModeData(dhcp4, &config); 326 | if (status != 0) 327 | { 328 | printk("UEFI DHCP get mode failed? %d\n", status); 329 | return; 330 | } 331 | 332 | if (config.State != Dhcp4Bound) 333 | { 334 | printk("UEFI DHCP not bound to an address, skipping\n"); 335 | return; 336 | } 337 | 338 | printk("uefi-dhcp ip=%d.%d.%d.%d router=%d.%d.%d.%d subnet=%d.%d.%d.%d state=%d\n", 339 | config.ClientAddress.Addr[0], 340 | config.ClientAddress.Addr[1], 341 | config.ClientAddress.Addr[2], 342 | config.ClientAddress.Addr[3], 343 | config.RouterAddress.Addr[0], 344 | config.RouterAddress.Addr[1], 345 | config.RouterAddress.Addr[2], 346 | config.RouterAddress.Addr[3], 347 | config.SubnetMask.Addr[0], 348 | config.SubnetMask.Addr[1], 349 | config.SubnetMask.Addr[2], 350 | config.SubnetMask.Addr[3], 351 | nic->uefi_nic->Mode->State 352 | ); 353 | 354 | uefi_nic_set_address( 355 | nic, 356 | &config.ClientAddress, 357 | &config.SubnetMask, 358 | &config.RouterAddress 359 | ); 360 | } 361 | 362 | static int uefi_nic_create(int id, EFI_HANDLE handle, EFI_SIMPLE_NETWORK_PROTOCOL * uefi_nic) 363 | { 364 | struct net_device * dev; 365 | uefi_nic_t * nic; 366 | dev = alloc_etherdev(sizeof(uefi_nic_t)); 367 | 368 | if (!dev) 369 | { 370 | printk("unable to allocate net dev\n"); 371 | return -1; 372 | } 373 | 374 | nic = netdev_priv(dev); 375 | if (!nic) 376 | { 377 | printk("private data is null?\n"); 378 | return -1; 379 | } 380 | 381 | memset(nic, 0, sizeof(*nic)); 382 | 383 | spin_lock_init(&nic->lock); 384 | nic->dev = dev; 385 | nic->uefi_nic = uefi_nic; 386 | nic->uefi_handle = handle; 387 | nic->id = id; 388 | nic->up = 0; 389 | nic->rx_skb = NULL; 390 | 391 | memcpy(dev->dev_addr, uefi_nic->Mode->CurrentAddress.Addr, ETH_ALEN); 392 | dev->netdev_ops = &uefi_nic_ops; 393 | 394 | register_netdevice(dev); 395 | 396 | printk("%d: type=%d media=%d addr=%02x:%02x:%02x:%02x:%02x:%02x\n", 397 | nic->id, 398 | uefi_nic->Mode->IfType, 399 | uefi_nic->Mode->MediaPresent, 400 | dev->dev_addr[0], 401 | dev->dev_addr[1], 402 | dev->dev_addr[2], 403 | dev->dev_addr[3], 404 | dev->dev_addr[4], 405 | dev->dev_addr[5] 406 | ); 407 | 408 | // store it in our array of active NIC 409 | uefi_nics[uefi_nic_count++] = nic; 410 | 411 | uefi_nic_autoconfig(nic); 412 | 413 | return 0; 414 | } 415 | 416 | 417 | /* 418 | static struct rtnl_link_ops uefi_nic_link_ops = { 419 | .kind = "uefi", 420 | .setup = uefi_nic_setup, 421 | .validate = uefi_nic_validate, 422 | }; 423 | */ 424 | 425 | int uefi_nic_init(void) 426 | { 427 | EFI_HANDLE handles[64]; 428 | int handle_count = uefi_locate_handles(&EFI_SIMPLE_NETWORK_PROTOCOL_GUID, handles, 64); 429 | 430 | printk("found %d NIC handles\n", handle_count); 431 | 432 | for(int i = 0 ; i < handle_count ; i++) 433 | { 434 | EFI_HANDLE handle = handles[i]; 435 | 436 | EFI_SIMPLE_NETWORK_PROTOCOL * nic = uefi_handle_protocol(&EFI_SIMPLE_NETWORK_PROTOCOL_GUID, handle); 437 | if (!nic) 438 | continue; 439 | 440 | uefi_nic_create(i, handle, nic); 441 | 442 | // only create the first one; they are duplicates? 443 | break; 444 | } 445 | 446 | timer_setup(&uefi_rx_timer, uefi_net_poll, 0); 447 | mod_timer(&uefi_rx_timer, jiffies + msecs_to_jiffies(10)); 448 | 449 | return 0; 450 | } 451 | 452 | int uefi_nic_exit(void) 453 | { 454 | for(int i = 0 ; i < uefi_nic_count ; i++) 455 | { 456 | uefi_nic_t * nic = uefi_nics[i]; 457 | printk("uefi%d: shutdown nic\n", i); 458 | nic->uefi_nic->Shutdown(nic->uefi_nic); 459 | } 460 | 461 | return 0; 462 | } 463 | -------------------------------------------------------------------------------- /module/efinet.h: -------------------------------------------------------------------------------- 1 | #ifndef _EFINET_H 2 | #define _EFINET_H 3 | 4 | 5 | /*++ 6 | Copyright (c) 1999 Intel Corporation 7 | 8 | Module Name: 9 | efinet.h 10 | 11 | Abstract: 12 | EFI Simple Network protocol 13 | 14 | Revision History 15 | --*/ 16 | 17 | 18 | /////////////////////////////////////////////////////////////////////////////// 19 | // 20 | // Simple Network Protocol 21 | // 22 | 23 | #define EFI_SIMPLE_NETWORK_PROTOCOL_GUID EFI_GUID( 0xA19832B9, 0xAC25, 0x11D3, 0x9A, 0x2D, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D ) 24 | 25 | INTERFACE_DECL(_EFI_SIMPLE_NETWORK_PROTOCOL); 26 | 27 | /////////////////////////////////////////////////////////////////////////////// 28 | // 29 | 30 | typedef struct { 31 | // 32 | // Total number of frames received. Includes frames with errors and 33 | // dropped frames. 34 | // 35 | UINT64 RxTotalFrames; 36 | 37 | // 38 | // Number of valid frames received and copied into receive buffers. 39 | // 40 | UINT64 RxGoodFrames; 41 | 42 | // 43 | // Number of frames below the minimum length for the media. 44 | // This would be <64 for ethernet. 45 | // 46 | UINT64 RxUndersizeFrames; 47 | 48 | // 49 | // Number of frames longer than the maxminum length for the 50 | // media. This would be >1500 for ethernet. 51 | // 52 | UINT64 RxOversizeFrames; 53 | 54 | // 55 | // Valid frames that were dropped because receive buffers were full. 56 | // 57 | UINT64 RxDroppedFrames; 58 | 59 | // 60 | // Number of valid unicast frames received and not dropped. 61 | // 62 | UINT64 RxUnicastFrames; 63 | 64 | // 65 | // Number of valid broadcast frames received and not dropped. 66 | // 67 | UINT64 RxBroadcastFrames; 68 | 69 | // 70 | // Number of valid mutlicast frames received and not dropped. 71 | // 72 | UINT64 RxMulticastFrames; 73 | 74 | // 75 | // Number of frames w/ CRC or alignment errors. 76 | // 77 | UINT64 RxCrcErrorFrames; 78 | 79 | // 80 | // Total number of bytes received. Includes frames with errors 81 | // and dropped frames. 82 | // 83 | UINT64 RxTotalBytes; 84 | 85 | // 86 | // Transmit statistics. 87 | // 88 | UINT64 TxTotalFrames; 89 | UINT64 TxGoodFrames; 90 | UINT64 TxUndersizeFrames; 91 | UINT64 TxOversizeFrames; 92 | UINT64 TxDroppedFrames; 93 | UINT64 TxUnicastFrames; 94 | UINT64 TxBroadcastFrames; 95 | UINT64 TxMulticastFrames; 96 | UINT64 TxCrcErrorFrames; 97 | UINT64 TxTotalBytes; 98 | 99 | // 100 | // Number of collisions detection on this subnet. 101 | // 102 | UINT64 Collisions; 103 | 104 | // 105 | // Number of frames destined for unsupported protocol. 106 | // 107 | UINT64 UnsupportedProtocol; 108 | 109 | } EFI_NETWORK_STATISTICS; 110 | 111 | /////////////////////////////////////////////////////////////////////////////// 112 | // 113 | 114 | typedef enum { 115 | EfiSimpleNetworkStopped, 116 | EfiSimpleNetworkStarted, 117 | EfiSimpleNetworkInitialized, 118 | EfiSimpleNetworkMaxState 119 | } EFI_SIMPLE_NETWORK_STATE; 120 | 121 | /////////////////////////////////////////////////////////////////////////////// 122 | // 123 | 124 | #define EFI_SIMPLE_NETWORK_RECEIVE_UNICAST 0x01 125 | #define EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST 0x02 126 | #define EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST 0x04 127 | #define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS 0x08 128 | #define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST 0x10 129 | 130 | /////////////////////////////////////////////////////////////////////////////// 131 | // 132 | 133 | #define EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT 0x01 134 | #define EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT 0x02 135 | #define EFI_SIMPLE_NETWORK_COMMAND_INTERRUPT 0x04 136 | #define EFI_SIMPLE_NETWORK_SOFTWARE_INTERRUPT 0x08 137 | 138 | /////////////////////////////////////////////////////////////////////////////// 139 | // 140 | #define MAX_MCAST_FILTER_CNT 16 141 | typedef struct { 142 | UINT32 State; 143 | UINT32 HwAddressSize; 144 | UINT32 MediaHeaderSize; 145 | UINT32 MaxPacketSize; 146 | UINT32 NvRamSize; 147 | UINT32 NvRamAccessSize; 148 | UINT32 ReceiveFilterMask; 149 | UINT32 ReceiveFilterSetting; 150 | UINT32 MaxMCastFilterCount; 151 | UINT32 MCastFilterCount; 152 | EFI_MAC_ADDRESS MCastFilter[MAX_MCAST_FILTER_CNT]; 153 | EFI_MAC_ADDRESS CurrentAddress; 154 | EFI_MAC_ADDRESS BroadcastAddress; 155 | EFI_MAC_ADDRESS PermanentAddress; 156 | UINT8 IfType; 157 | BOOLEAN MacAddressChangeable; 158 | BOOLEAN MultipleTxSupported; 159 | BOOLEAN MediaPresentSupported; 160 | BOOLEAN MediaPresent; 161 | } EFI_SIMPLE_NETWORK_MODE; 162 | 163 | /////////////////////////////////////////////////////////////////////////////// 164 | // 165 | 166 | typedef 167 | EFI_STATUS 168 | (EFIAPI *EFI_SIMPLE_NETWORK_START) ( 169 | IN struct _EFI_SIMPLE_NETWORK_PROTOCOL *This 170 | ); 171 | 172 | /////////////////////////////////////////////////////////////////////////////// 173 | // 174 | 175 | typedef 176 | EFI_STATUS 177 | (EFIAPI *EFI_SIMPLE_NETWORK_STOP) ( 178 | IN struct _EFI_SIMPLE_NETWORK_PROTOCOL *This 179 | ); 180 | 181 | /////////////////////////////////////////////////////////////////////////////// 182 | // 183 | 184 | typedef 185 | EFI_STATUS 186 | (EFIAPI *EFI_SIMPLE_NETWORK_INITIALIZE) ( 187 | IN struct _EFI_SIMPLE_NETWORK_PROTOCOL *This, 188 | IN UINTN ExtraRxBufferSize OPTIONAL, 189 | IN UINTN ExtraTxBufferSize OPTIONAL 190 | ); 191 | 192 | /////////////////////////////////////////////////////////////////////////////// 193 | // 194 | 195 | typedef 196 | EFI_STATUS 197 | (EFIAPI *EFI_SIMPLE_NETWORK_RESET) ( 198 | IN struct _EFI_SIMPLE_NETWORK_PROTOCOL *This, 199 | IN BOOLEAN ExtendedVerification 200 | ); 201 | 202 | /////////////////////////////////////////////////////////////////////////////// 203 | // 204 | 205 | typedef 206 | EFI_STATUS 207 | (EFIAPI *EFI_SIMPLE_NETWORK_SHUTDOWN) ( 208 | IN struct _EFI_SIMPLE_NETWORK_PROTOCOL *This 209 | ); 210 | 211 | /////////////////////////////////////////////////////////////////////////////// 212 | // 213 | 214 | typedef 215 | EFI_STATUS 216 | (EFIAPI *EFI_SIMPLE_NETWORK_RECEIVE_FILTERS) ( 217 | IN struct _EFI_SIMPLE_NETWORK_PROTOCOL *This, 218 | IN UINT32 Enable, 219 | IN UINT32 Disable, 220 | IN BOOLEAN ResetMCastFilter, 221 | IN UINTN MCastFilterCnt OPTIONAL, 222 | IN EFI_MAC_ADDRESS *MCastFilter OPTIONAL 223 | ); 224 | 225 | /////////////////////////////////////////////////////////////////////////////// 226 | // 227 | 228 | typedef 229 | EFI_STATUS 230 | (EFIAPI *EFI_SIMPLE_NETWORK_STATION_ADDRESS) ( 231 | IN struct _EFI_SIMPLE_NETWORK_PROTOCOL *This, 232 | IN BOOLEAN Reset, 233 | IN EFI_MAC_ADDRESS *New OPTIONAL 234 | ); 235 | 236 | /////////////////////////////////////////////////////////////////////////////// 237 | // 238 | 239 | typedef 240 | EFI_STATUS 241 | (EFIAPI *EFI_SIMPLE_NETWORK_STATISTICS) ( 242 | IN struct _EFI_SIMPLE_NETWORK_PROTOCOL *This, 243 | IN BOOLEAN Reset, 244 | IN OUT UINTN *StatisticsSize OPTIONAL, 245 | OUT EFI_NETWORK_STATISTICS *StatisticsTable OPTIONAL 246 | ); 247 | 248 | /////////////////////////////////////////////////////////////////////////////// 249 | // 250 | 251 | typedef 252 | EFI_STATUS 253 | (EFIAPI *EFI_SIMPLE_NETWORK_MCAST_IP_TO_MAC) ( 254 | IN struct _EFI_SIMPLE_NETWORK_PROTOCOL *This, 255 | IN BOOLEAN IPv6, 256 | IN EFI_IP_ADDRESS *IP, 257 | OUT EFI_MAC_ADDRESS *MAC 258 | ); 259 | 260 | /////////////////////////////////////////////////////////////////////////////// 261 | // 262 | 263 | typedef 264 | EFI_STATUS 265 | (EFIAPI *EFI_SIMPLE_NETWORK_NVDATA) ( 266 | IN struct _EFI_SIMPLE_NETWORK_PROTOCOL *This, 267 | IN BOOLEAN ReadWrite, 268 | IN UINTN Offset, 269 | IN UINTN BufferSize, 270 | IN OUT VOID *Buffer 271 | ); 272 | 273 | /////////////////////////////////////////////////////////////////////////////// 274 | // 275 | 276 | typedef 277 | EFI_STATUS 278 | (EFIAPI *EFI_SIMPLE_NETWORK_GET_STATUS) ( 279 | IN struct _EFI_SIMPLE_NETWORK_PROTOCOL *This, 280 | OUT UINT32 *InterruptStatus OPTIONAL, 281 | OUT VOID **TxBuf OPTIONAL 282 | ); 283 | 284 | /////////////////////////////////////////////////////////////////////////////// 285 | // 286 | 287 | typedef 288 | EFI_STATUS 289 | (EFIAPI *EFI_SIMPLE_NETWORK_TRANSMIT) ( 290 | IN struct _EFI_SIMPLE_NETWORK_PROTOCOL *This, 291 | IN UINTN HeaderSize, 292 | IN UINTN BufferSize, 293 | IN VOID *Buffer, 294 | IN EFI_MAC_ADDRESS *SrcAddr OPTIONAL, 295 | IN EFI_MAC_ADDRESS *DestAddr OPTIONAL, 296 | IN UINT16 *Protocol OPTIONAL 297 | ); 298 | 299 | /////////////////////////////////////////////////////////////////////////////// 300 | // 301 | 302 | typedef 303 | EFI_STATUS 304 | (EFIAPI *EFI_SIMPLE_NETWORK_RECEIVE) ( 305 | IN struct _EFI_SIMPLE_NETWORK_PROTOCOL *This, 306 | OUT UINTN *HeaderSize OPTIONAL, 307 | IN OUT UINTN *BufferSize, 308 | OUT VOID *Buffer, 309 | OUT EFI_MAC_ADDRESS *SrcAddr OPTIONAL, 310 | OUT EFI_MAC_ADDRESS *DestAddr OPTIONAL, 311 | OUT UINT16 *Protocol OPTIONAL 312 | ); 313 | 314 | /////////////////////////////////////////////////////////////////////////////// 315 | // 316 | 317 | #define EFI_SIMPLE_NETWORK_PROTOCOL_REVISION 0x00010000 318 | #define EFI_SIMPLE_NETWORK_INTERFACE_REVISION EFI_SIMPLE_NETWORK_PROTOCOL_REVISION 319 | 320 | typedef struct _EFI_SIMPLE_NETWORK_PROTOCOL { 321 | UINT64 Revision; 322 | EFI_SIMPLE_NETWORK_START Start; 323 | EFI_SIMPLE_NETWORK_STOP Stop; 324 | EFI_SIMPLE_NETWORK_INITIALIZE Initialize; 325 | EFI_SIMPLE_NETWORK_RESET Reset; 326 | EFI_SIMPLE_NETWORK_SHUTDOWN Shutdown; 327 | EFI_SIMPLE_NETWORK_RECEIVE_FILTERS ReceiveFilters; 328 | EFI_SIMPLE_NETWORK_STATION_ADDRESS StationAddress; 329 | EFI_SIMPLE_NETWORK_STATISTICS Statistics; 330 | EFI_SIMPLE_NETWORK_MCAST_IP_TO_MAC MCastIpToMac; 331 | EFI_SIMPLE_NETWORK_NVDATA NvData; 332 | EFI_SIMPLE_NETWORK_GET_STATUS GetStatus; 333 | EFI_SIMPLE_NETWORK_TRANSMIT Transmit; 334 | EFI_SIMPLE_NETWORK_RECEIVE Receive; 335 | EFI_EVENT WaitForPacket; 336 | EFI_SIMPLE_NETWORK_MODE *Mode; 337 | } EFI_SIMPLE_NETWORK_PROTOCOL; 338 | 339 | // Note: Because it conflicted with the EDK2 struct name, the 340 | // 'EFI_SIMPLE_NETWORK_PROTOCOL' GUID definition, from older 341 | // versions of gnu-efi, is now obsoleted. 342 | // Use 'EFI_SIMPLE_NETWORK_PROTOCOL_GUID' instead. 343 | 344 | typedef struct _EFI_SIMPLE_NETWORK_PROTOCOL _EFI_SIMPLE_NETWORK; 345 | typedef EFI_SIMPLE_NETWORK_PROTOCOL EFI_SIMPLE_NETWORK; 346 | 347 | #endif /* _EFINET_H */ 348 | -------------------------------------------------------------------------------- /module/efiwrapper.c: -------------------------------------------------------------------------------- 1 | /** UEFI Wrappers for common operations 2 | * 3 | */ 4 | 5 | #include 6 | #include "efiwrapper.h" 7 | 8 | static uint64_t uefi_pagetable_0; 9 | static efi_system_table_t * gST; 10 | efi_boot_services_t * gBS; 11 | static EFI_HANDLE kernel_handle; 12 | 13 | int uefi_memory_map_add(void) 14 | { 15 | uint64_t cr3_phys; 16 | volatile uint64_t * linux_pagetable; 17 | uint64_t linux_pagetable_0; 18 | 19 | if (uefi_pagetable_0 == 0) 20 | { 21 | const uint64_t * const uefi_context = phys_to_virt(0x100); // hack! 22 | const uint64_t uefi_cr3 = uefi_context[0x40/8]; 23 | const uint64_t * uefi_pagetable; 24 | 25 | if (uefi_context[0xa0/8] != 0xdecafbad) 26 | { 27 | printk("uefi context bad magic %llx, things will probably break\n", uefi_context[0xa0/8]); 28 | return -1; 29 | } 30 | 31 | uefi_pagetable = ioremap(uefi_cr3 & ~0xFFF, 0x1000); 32 | uefi_pagetable_0 = uefi_pagetable[0]; 33 | kernel_handle = (void*) uefi_context[0x90/8]; // %rdi passed to the efi stub 34 | gST = (void*) uefi_context[0x98/8]; // %rsi passed to the efi stub 35 | 36 | printk("UEFI CR3=%016llx CR3[0]=%016llx gST=%016llx\n", uefi_cr3, uefi_pagetable_0, (uint64_t) gST); 37 | 38 | } 39 | 40 | // get our current CR3, masking out the ASID, to get the 41 | // pointer to our page table 42 | __asm__ __volatile__("mov %%cr3, %0" : "=r"(cr3_phys)); 43 | 44 | linux_pagetable = phys_to_virt(cr3_phys & ~0xFFF); 45 | linux_pagetable_0 = linux_pagetable[0]; 46 | 47 | 48 | if (linux_pagetable_0 != 0 49 | && linux_pagetable_0 != uefi_pagetable_0) 50 | { 51 | printk("UH OH: linux has something mapped at 0x0: CR3=%016llx CR3[0]=%016llx\n", cr3_phys, linux_pagetable_0); 52 | return -1; 53 | } 54 | 55 | // poke in the entry for the UEFI page table that we've stored 56 | // reusing UEFI's linear map of low memory 57 | linux_pagetable[0] = uefi_pagetable_0; 58 | 59 | // are we supposed to force a TLB flush or something? 60 | __asm__ __volatile__("mov %0, %%cr3" : : "r"(cr3_phys) : "memory"); 61 | 62 | 63 | // we can use the UEFI address space pointers now 64 | gBS = gST->boottime; 65 | 66 | if (gBS == 0) 67 | { 68 | printk("UH OH: boot services is a null pointer?\n"); 69 | return -1; 70 | } 71 | 72 | #if 0 // debug on 5.15 73 | printk("gST=%llx\n", (uint64_t) gST); 74 | for(int i = 0 ; i < 64 ; i++) 75 | printk("%08x", i[(uint32_t*)gST]); 76 | printk("\n"); 77 | 78 | printk("gBS=%llx\n", (uint64_t) gBS); 79 | for(int i = 0 ; i < 64 ; i++) 80 | printk("%08x", i[(uint32_t*)gBS]); 81 | printk("\n"); 82 | #endif 83 | 84 | // success! 85 | return 0; 86 | } 87 | 88 | 89 | void * uefi_alloc(size_t len) 90 | { 91 | UINTN pages = (len + 4095) / 4096; 92 | EFI_PHYSICAL_ADDRESS uefi_buffer; 93 | 94 | efi_status_t EFIAPI (*allocate_pages)(int, int, unsigned long, efi_physical_addr_t *) 95 | = (void*) gBS->allocate_pages; 96 | 97 | 98 | int status = allocate_pages( 99 | EFI_ALLOCATE_ANY_PAGES, 100 | EFI_BOOT_SERVICES_DATA, 101 | pages, 102 | &uefi_buffer 103 | ); 104 | 105 | if (status != 0) 106 | return NULL; 107 | 108 | return (void*) uefi_buffer; 109 | } 110 | 111 | #define EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID EFI_GUID(0x8b843e20, 0x8132, 0x4852, 0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c) 112 | 113 | typedef CHAR16* 114 | (EFIAPI *EFI_DEVICE_PATH_TO_TEXT_PATH)( 115 | IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, 116 | IN BOOLEAN DisplayOnly, 117 | IN BOOLEAN AllowShortcuts 118 | ); 119 | 120 | typedef struct { 121 | void * ConvertDeviceNodeToText; 122 | EFI_DEVICE_PATH_TO_TEXT_PATH ConvertDevicePathToText; 123 | } EFI_DEVICE_PATH_TO_TEXT_PROTOCOL; 124 | 125 | char * uefi_device_path_to_name(EFI_HANDLE dev_handle) 126 | { 127 | EFI_DEVICE_PATH_TO_TEXT_PROTOCOL * dp2txt = uefi_locate_and_handle_protocol(&EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID); 128 | EFI_DEVICE_PATH_PROTOCOL * dp = uefi_handle_protocol(&EFI_DEVICE_PATH_PROTOCOL_GUID, dev_handle); 129 | char * dp2 = NULL; // wide-char return 130 | static char buf[256]; 131 | 132 | if (!dp2txt || !dp) 133 | return "LocateHandle DevicePath failed"; 134 | 135 | dp2 = (char*) dp2txt->ConvertDevicePathToText(dp, 0, 0); 136 | if (!dp2) 137 | return "ConvertDevicePathToText failed"; 138 | 139 | // convert it to a normal string, ensuring there is a nul terminator at the end 140 | for(int i = 0 ; i < sizeof(buf)-1 ; i++) 141 | { 142 | uint16_t c = dp2[2*i]; 143 | buf[i] = c; 144 | if (c == 0) 145 | break; 146 | } 147 | 148 | return buf; 149 | } 150 | 151 | 152 | int uefi_locate_handles(efi_guid_t * guid, EFI_HANDLE * handles, int max_handles) 153 | { 154 | unsigned long handlesize = max_handles * sizeof(*handles); 155 | efi_status_t EFIAPI (*locate_handle)(int, efi_guid_t *, void *, 156 | unsigned long *, efi_handle_t *) = (void*) gBS->locate_handle; 157 | 158 | // printk("gBS=%llx locate=%llx\n", (uint64_t) gBS, (uint64_t) locate_handle); 159 | // if (locate_handle == 0) 160 | // print_hex_dump_bytes("bootservices", KERN_ERR, gBS, 512); 161 | 162 | int status = locate_handle( 163 | EFI_LOCATE_BY_PROTOCOL, 164 | guid, 165 | NULL, 166 | &handlesize, 167 | handles 168 | ); 169 | 170 | if (status != 0) 171 | return -1; 172 | 173 | // return the count of handles 174 | return handlesize / sizeof(*handles); 175 | } 176 | 177 | void * uefi_locate_and_handle_protocol(efi_guid_t * guid) 178 | { 179 | void * handles[1]; 180 | int count = uefi_locate_handles(guid, handles, 1); 181 | if (count < 1) 182 | return NULL; 183 | return uefi_handle_protocol(guid, handles[0]); 184 | } 185 | 186 | void * uefi_handle_protocol(efi_guid_t * guid, EFI_HANDLE handle) 187 | { 188 | efi_status_t EFIAPI (*handle_protocol)(efi_handle_t, efi_guid_t *, void **) = (void*) gBS->handle_protocol; 189 | 190 | void * proto = NULL; 191 | int status = handle_protocol( 192 | handle, 193 | guid, 194 | &proto 195 | ); 196 | 197 | if (status != 0) 198 | return NULL; 199 | 200 | return proto; 201 | } 202 | 203 | typedef 204 | EFI_STATUS 205 | (EFIAPI *EFI_IMAGE_LOAD) ( 206 | IN BOOLEAN BootPolicy, 207 | IN EFI_HANDLE ParentImageHandle, 208 | IN EFI_DEVICE_PATH *FilePath, 209 | IN VOID *SourceBuffer OPTIONAL, 210 | IN UINTN SourceSize, 211 | OUT EFI_HANDLE *ImageHandle 212 | ); 213 | 214 | typedef 215 | EFI_STATUS 216 | (EFIAPI *EFI_IMAGE_START) ( 217 | IN EFI_HANDLE ImageHandle, 218 | OUT UINTN *ExitDataSize, 219 | OUT CHAR16 **ExitData OPTIONAL 220 | ); 221 | 222 | 223 | EFI_HANDLE uefi_load_and_start_image(void * buf, size_t len, EFI_DEVICE_PATH * filepath) 224 | { 225 | EFI_IMAGE_LOAD load_image = (void*) gBS->load_image; 226 | EFI_IMAGE_START start_image = (void*) gBS->start_image; 227 | EFI_HANDLE image_handle; 228 | CHAR16 * exit_data; 229 | UINTN exit_data_size; 230 | int status; 231 | 232 | status = load_image( 233 | 0, 234 | kernel_handle, 235 | filepath, 236 | buf, 237 | len, 238 | &image_handle 239 | ); 240 | 241 | if (status != 0) 242 | return NULL; 243 | 244 | status = start_image(image_handle, &exit_data_size, &exit_data); 245 | 246 | printk("uefi_loader: status=%d exit_data=%lld\n", status, exit_data_size); 247 | if (status != 0) 248 | return NULL; 249 | 250 | return image_handle; 251 | } 252 | 253 | 254 | void * uefi_alloc_and_read_file(const char * filename_in, size_t * size_out) 255 | { 256 | loff_t file_size; 257 | loff_t pos = 0; 258 | void * image; 259 | struct file * file; 260 | ssize_t rc; 261 | 262 | char filename[256]; 263 | unsigned len = 0; 264 | 265 | // echo /bin/test.gpt > /sys/firmware/efi/ramdisk 266 | // trim trailing whitespace from the filename, 267 | while(len < sizeof(filename)) 268 | { 269 | char c = *filename_in++; 270 | filename[len++] = c; 271 | if (c == '\0') 272 | break; 273 | } 274 | 275 | // len is at least 1 and points to the nul at the end 276 | len--; 277 | 278 | while(len > 1) 279 | { 280 | unsigned char c = filename[--len]; 281 | if (c != ' ' && c != '\n' && c != '\r' && c != '\t') 282 | break; 283 | filename[len] = '\0'; 284 | } 285 | 286 | file = filp_open(filename, O_RDONLY, 0); 287 | if (file == NULL) 288 | { 289 | printk("uefi_loader: unable to open '%s'\n", filename); 290 | goto fail_open; 291 | } 292 | 293 | file_size = i_size_read(file_inode(file)); 294 | printk("uefi_read_file: %s => %lld\n", filename, file_size); 295 | 296 | // use UEFI to allocate the memory, which is a bit bonkers 297 | uefi_memory_map_add(); 298 | 299 | image = uefi_alloc(file_size); 300 | if (!image) 301 | { 302 | printk("uefi_loader: could not allocate %lld bytes", file_size); 303 | goto fail_alloc; 304 | } 305 | 306 | rc = kernel_read(file, image, file_size, &pos); 307 | if (rc != file_size) 308 | { 309 | printk("uefi_loader: did not read entire file: %zu\n", rc); 310 | goto fail_read; 311 | } 312 | 313 | filp_close(file, NULL); 314 | if (size_out) 315 | *size_out = file_size; 316 | 317 | return image; 318 | 319 | fail_read: 320 | // todo? should free image 321 | fail_alloc: 322 | filp_close(file, NULL); 323 | fail_open: 324 | return NULL; 325 | } 326 | -------------------------------------------------------------------------------- /module/efiwrapper.h: -------------------------------------------------------------------------------- 1 | /** UEFI Block Device. 2 | * 3 | * Implements a simplistic block device that calls back 4 | * into the UEFI BlockDeviceProtocol. 5 | */ 6 | #ifndef _uefiblockdev_efi_wrapper_h_ 7 | #define _uefiblockdev_efi_wrapper_h_ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | //#include "efistub.h" 14 | 15 | #define DRIVER_NAME "uefidev" 16 | #define DRIVER_VERSION "v0.1" 17 | #define DRIVER_AUTHOR "Trammell Hudson" 18 | #define DRIVER_DESC "UEFI Device Driver" 19 | 20 | 21 | /* Things to make edk2 EFI headers work */ 22 | typedef uint64_t EFI_LBA; 23 | typedef int EFI_STATUS; 24 | typedef uint8_t BOOLEAN; 25 | typedef uint64_t UINTN; 26 | typedef uint8_t UINT8; 27 | typedef uint16_t UINT16; 28 | typedef uint32_t UINT32; 29 | typedef uint64_t UINT64; 30 | typedef int8_t INT8; 31 | typedef int16_t INT16; 32 | typedef int32_t INT32; 33 | typedef int64_t INT64; 34 | typedef void VOID; 35 | typedef void * EFI_HANDLE; 36 | typedef char CHAR8; 37 | typedef uint16_t CHAR16; 38 | typedef uint64_t EFI_PHYSICAL_ADDRESS; 39 | typedef efi_guid_t EFI_GUID; 40 | 41 | #define CONST const 42 | #define IN /* in */ 43 | #define OUT /* out */ 44 | #define OPTIONAL /* optional */ 45 | #define EFIAPI __attribute__((ms_abi)) 46 | 47 | typedef struct { 48 | UINT16 Year; // 1998 - 20XX 49 | UINT8 Month; // 1 - 12 50 | UINT8 Day; // 1 - 31 51 | UINT8 Hour; // 0 - 23 52 | UINT8 Minute; // 0 - 59 53 | UINT8 Second; // 0 - 59 54 | UINT8 Pad1; 55 | UINT32 Nanosecond; // 0 - 999,999,999 56 | INT16 TimeZone; // -1440 to 1440 or 2047 57 | UINT8 Daylight; 58 | UINT8 Pad2; 59 | } EFI_TIME; 60 | 61 | 62 | typedef struct { 63 | UINT8 Addr[4]; 64 | } EFI_IPv4_ADDRESS; 65 | 66 | typedef struct { 67 | UINT8 Addr[16]; 68 | } EFI_IPv6_ADDRESS; 69 | 70 | typedef struct { 71 | UINT8 Addr[32]; 72 | } EFI_MAC_ADDRESS; 73 | 74 | typedef union { 75 | UINT32 Addr[4]; 76 | EFI_IPv4_ADDRESS v4; 77 | EFI_IPv6_ADDRESS v6; 78 | } EFI_IP_ADDRESS; 79 | 80 | #define EFI_DEVICE_PATH_PROTOCOL_GUID EFI_GUID(0x9576e91, 0x6d3f, 0x11d2, 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b) 81 | typedef void * EFI_DEVICE_PATH_PROTOCOL; 82 | 83 | #define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID EFI_GUID( 0x964e5b22, 0x6459, 0x11d2, 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b) 84 | 85 | 86 | typedef void * EFI_DEVICE_PATH; 87 | 88 | typedef void * EFI_EVENT; 89 | 90 | 91 | #define INTERFACE_DECL(x) struct x 92 | 93 | #ifndef EFI_LOCATE_BY_PROTOCOL 94 | #define EFI_LOCATE_BY_PROTOCOL 2 95 | #endif 96 | 97 | /* Helper functions to make it bearable to call EFI functions */ 98 | extern efi_boot_services_t * gBS; 99 | 100 | extern int uefi_memory_map_add(void); 101 | extern void * uefi_alloc(size_t len); 102 | extern char * uefi_device_path_to_name(EFI_HANDLE dev_handle); 103 | extern int uefi_locate_handles(efi_guid_t * guid, EFI_HANDLE * handles, int max_handles); 104 | extern EFI_HANDLE uefi_locate_handle(efi_guid_t * guid); 105 | extern void * uefi_handle_protocol(efi_guid_t * guid, EFI_HANDLE handle); 106 | extern void * uefi_locate_and_handle_protocol(efi_guid_t * guid); 107 | extern EFI_HANDLE uefi_load_and_start_image(void * buf, size_t len, EFI_DEVICE_PATH * filepath); 108 | extern void * uefi_alloc_and_read_file(const char * filename, size_t * size_out); 109 | 110 | extern int uefi_register_protocol_callback( 111 | EFI_GUID * guid, 112 | void (*handler)(void*), 113 | void * context 114 | ); 115 | 116 | /* Device driver init functions go here */ 117 | extern int uefi_loader_init(void); 118 | extern int uefi_ramdisk_init(void); 119 | extern int uefi_blockdev_init(void); 120 | extern int uefi_nic_init(void); 121 | extern int uefi_nic_exit(void); 122 | extern int uefi_tpm_init(void); 123 | 124 | #endif 125 | 126 | -------------------------------------------------------------------------------- /module/event.c: -------------------------------------------------------------------------------- 1 | /* UEFI event and callback wrapper 2 | */ 3 | #include 4 | #include "efiwrapper.h" 5 | 6 | typedef 7 | VOID 8 | (EFIAPI *EFI_EVENT_NOTIFY) ( 9 | IN EFI_EVENT Event, 10 | IN VOID *Context 11 | ); 12 | 13 | typedef UINTN EFI_TPL; 14 | #define TPL_APPLICATION 4 15 | #define TPL_CALLBACK 8 16 | #define TPL_NOTIFY 16 17 | #define TPL_HIGH_LEVEL 31 18 | #define EFI_TPL_APPLICATION TPL_APPLICATION 19 | #define EFI_TPL_CALLBACK TPL_CALLBACK 20 | #define EFI_TPL_NOTIFY TPL_NOTIFY 21 | #define EFI_TPL_HIGH_LEVEL TPL_HIGH_LEVEL 22 | 23 | #define EVT_TIMER 0x80000000 24 | #define EVT_RUNTIME 0x40000000 25 | #define EVT_RUNTIME_CONTEXT 0x20000000 26 | 27 | #define EVT_NOTIFY_WAIT 0x00000100 28 | #define EVT_NOTIFY_SIGNAL 0x00000200 29 | 30 | #define EVT_SIGNAL_EXIT_BOOT_SERVICES 0x00000201 31 | #define EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE 0x60000202 32 | 33 | #define EVT_EFI_SIGNAL_MASK 0x000000FF 34 | #define EVT_EFI_SIGNAL_MAX 4 35 | 36 | #define EFI_EVENT_TIMER EVT_TIMER 37 | #define EFI_EVENT_RUNTIME EVT_RUNTIME 38 | #define EFI_EVENT_RUNTIME_CONTEXT EVT_RUNTIME_CONTEXT 39 | #define EFI_EVENT_NOTIFY_WAIT EVT_NOTIFY_WAIT 40 | #define EFI_EVENT_NOTIFY_SIGNAL EVT_NOTIFY_SIGNAL 41 | #define EFI_EVENT_SIGNAL_EXIT_BOOT_SERVICES EVT_SIGNAL_EXIT_BOOT_SERVICES 42 | #define EFI_EVENT_SIGNAL_VIRTUAL_ADDRESS_CHANGE EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE 43 | #define EFI_EVENT_EFI_SIGNAL_MASK EVT_EFI_SIGNAL_MASK 44 | #define EFI_EVENT_EFI_SIGNAL_MAX EVT_EFI_SIGNAL_MAX 45 | 46 | 47 | typedef 48 | EFI_STATUS 49 | (EFIAPI *EFI_CREATE_EVENT) ( 50 | IN UINT32 Type, 51 | IN EFI_TPL NotifyTpl, 52 | IN EFI_EVENT_NOTIFY NotifyFunction, 53 | IN VOID *NotifyContext, 54 | OUT EFI_EVENT *Event 55 | ); 56 | 57 | typedef 58 | EFI_STATUS 59 | (EFIAPI *EFI_REGISTER_PROTOCOL_NOTIFY) ( 60 | IN EFI_GUID *Protocol, 61 | IN EFI_EVENT Event, 62 | OUT VOID **Registration 63 | ); 64 | 65 | typedef 66 | EFI_STATUS 67 | (EFIAPI *EFI_SIGNAL_EVENT) ( 68 | IN EFI_EVENT Event 69 | ); 70 | 71 | typedef struct { 72 | EFI_EVENT event; 73 | void * registration; 74 | void * context; 75 | void (*handler)(void *); 76 | } uefi_event_t; 77 | 78 | 79 | // UEFI calls back with MS ABI, so this must translate to Linux ABI 80 | static void EFIAPI uefi_event_callback(EFI_EVENT Event, VOID * context) 81 | { 82 | uefi_event_t * ev = context; 83 | ev->handler(ev->context); 84 | } 85 | 86 | int uefi_register_protocol_callback( 87 | EFI_GUID * guid, 88 | void (*handler)(void*), 89 | void * context 90 | ) 91 | { 92 | EFI_CREATE_EVENT create_event = (void*) gBS->create_event; 93 | EFI_REGISTER_PROTOCOL_NOTIFY register_protocol_notify = (void*) gBS->register_protocol_notify; 94 | EFI_SIGNAL_EVENT signal_event = (void*) gBS->signal_event; 95 | uefi_event_t * ev = kzalloc(sizeof(*ev), GFP_KERNEL); 96 | int status; 97 | 98 | ev->handler = handler; 99 | ev->context = context; 100 | 101 | status = create_event( 102 | EVT_NOTIFY_SIGNAL, 103 | TPL_CALLBACK, 104 | uefi_event_callback, 105 | ev, 106 | &ev->event 107 | ); 108 | printk("create event %d\n", status); 109 | 110 | status = register_protocol_notify( 111 | guid, 112 | ev->event, 113 | &ev->registration 114 | ); 115 | printk("register protocol %d\n", status); 116 | 117 | status = signal_event(ev->event); 118 | printk("signal event %d\n", status); 119 | 120 | return 0; 121 | } 122 | 123 | -------------------------------------------------------------------------------- /module/loader.c: -------------------------------------------------------------------------------- 1 | /* UEFI image loader 2 | * 3 | * Allow new EFI modules to be loaded with the Boot Services 4 | * loaded image protocol by catting the EFI image into 5 | * /sys/firmware/efi/loader 6 | */ 7 | #include 8 | #include "efiwrapper.h" 9 | 10 | 11 | static ssize_t store(struct kobject * kobj, struct kobj_attribute *attr, const char * buf, size_t count) 12 | { 13 | // open that file and attempt to read it 14 | size_t file_size; 15 | void * image = uefi_alloc_and_read_file(buf, &file_size); 16 | 17 | if (!image) 18 | return -1; 19 | 20 | printk("uefi_loader: starting %s (%zu bytes)\n", buf, file_size); 21 | if (!uefi_load_and_start_image(image, file_size, NULL)) 22 | return -1; 23 | 24 | return count; 25 | } 26 | 27 | 28 | static ssize_t show(struct kobject * kobj, struct kobj_attribute * attr, char * buf) 29 | { 30 | printk("uefi_loader: show %llx\n", (uint64_t) buf); 31 | return -1; 32 | } 33 | 34 | static struct kobj_attribute uefi_loader_attr 35 | = __ATTR(loader, 0600, show, store); 36 | 37 | int uefi_loader_init(void) 38 | { 39 | // efi_kobj is the global for /sys/firmware/efi 40 | int status; 41 | status = sysfs_create_file(efi_kobj, &uefi_loader_attr.attr); 42 | 43 | if (status < 0) 44 | { 45 | printk("uefi_loader: unable to create /sys/firmware/efi/loader: rc=%d\n", status); 46 | return -1; 47 | } 48 | 49 | printk("uefi_loader: created /sys/firmware/efi/loader\n"); 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /module/main.c: -------------------------------------------------------------------------------- 1 | /** UEFI Device Driver module 2 | * 3 | * Provides an interface to several different UEFI device drivers 4 | * as if they were normal Linux devices. 5 | */ 6 | 7 | #include 8 | #include 9 | #include "efiwrapper.h" 10 | #include "efinet.h" 11 | 12 | 13 | 14 | static int uefi_dev_init(void) 15 | { 16 | if (uefi_memory_map_add() < 0) 17 | return -1; 18 | 19 | if (uefi_loader_init() < 0) 20 | return -1; 21 | 22 | uefi_ramdisk_init(); 23 | 24 | #ifdef CONFIG_UEFIBLOCK 25 | if (uefi_blockdev_init() < 0) 26 | return -1; 27 | #endif 28 | 29 | #ifdef CONFIG_UEFINET 30 | if (uefi_nic_init() < 0) 31 | return -1; 32 | #endif 33 | 34 | #ifdef CONFIG_UEFITPM 35 | if (uefi_tpm_init() < 0) 36 | return -1; 37 | #endif 38 | 39 | // todo: tear down the other devices in the event of failure 40 | 41 | return 0; 42 | } 43 | 44 | module_init(uefi_dev_init); 45 | 46 | 47 | static void uefi_dev_exit(void) 48 | { 49 | // block does not need any shutdown 50 | // tpm does not require any shutdown 51 | // ramdisk explicitly does not want to shutdown 52 | 53 | #ifdef CONFIG_UEFINET 54 | uefi_nic_exit(); 55 | #endif 56 | } 57 | 58 | module_exit(uefi_dev_exit); 59 | 60 | 61 | MODULE_AUTHOR(DRIVER_AUTHOR); 62 | MODULE_DESCRIPTION(DRIVER_DESC); 63 | MODULE_LICENSE("GPL"); 64 | -------------------------------------------------------------------------------- /module/ramdisk.c: -------------------------------------------------------------------------------- 1 | /* UEFI ramdisk interface 2 | * 3 | * Create a ram disk given a disk image by catting into 4 | * /sys/firmware/efi/ramdisk 5 | */ 6 | #include 7 | #include "efiwrapper.h" 8 | #include "ramdisk.h" 9 | 10 | 11 | static ssize_t store(struct kobject * kobj, struct kobj_attribute *attr, const char * buf, size_t count) 12 | { 13 | // open that file and attempt to read it 14 | size_t file_size; 15 | void * image; 16 | EFI_DEVICE_PATH * devicepath; 17 | EFI_RAM_DISK_PROTOCOL * ramdisk; 18 | 19 | uefi_memory_map_add(); 20 | 21 | ramdisk = uefi_locate_and_handle_protocol(&EFI_RAMDISK_PROTOCOL_GUID); 22 | if (!ramdisk) 23 | { 24 | printk("uefi_ramdisk: vendor firmware has no RamDisk driver\n"); 25 | return -1; 26 | } 27 | 28 | image = uefi_alloc_and_read_file(buf, &file_size); 29 | if (!image) 30 | { 31 | printk("uefi_ramdisk: alloc and read failed\n"); 32 | return -1; 33 | } 34 | 35 | printk("uefi_ramdisk: %s %zu bytes", buf, file_size); 36 | ramdisk->Register( 37 | (UINT64) image, // physical address, since UEFI allocated it 38 | file_size, 39 | &EFI_RAMDISK_PROTOCOL_GUID, 40 | NULL, 41 | &devicepath 42 | ); 43 | 44 | return count; 45 | } 46 | 47 | 48 | static ssize_t show(struct kobject * kobj, struct kobj_attribute * attr, char * buf) 49 | { 50 | printk("uefi_ramdisk: show %llx\n", (uint64_t) buf); 51 | return -1; 52 | } 53 | 54 | static struct kobj_attribute uefi_ramdisk_attr 55 | = __ATTR(ramdisk, 0600, show, store); 56 | 57 | int uefi_ramdisk_init(void) 58 | { 59 | // efi_kobj is the global for /sys/firmware/efi 60 | int status; 61 | status = sysfs_create_file(efi_kobj, &uefi_ramdisk_attr.attr); 62 | 63 | if (status < 0) 64 | { 65 | printk("uefi_ramdisk: unable to create /sys/firmware/efi/loader: rc=%d\n", status); 66 | return -1; 67 | } 68 | 69 | printk("uefi_ramdisk: created /sys/firmware/efi/ramdisk\n"); 70 | return 0; 71 | } 72 | 73 | -------------------------------------------------------------------------------- /module/ramdisk.h: -------------------------------------------------------------------------------- 1 | #ifndef _uefi_ramdisk_h_ 2 | #define _uefi_ramdisk_h_ 3 | 4 | typedef struct _EFI_RAM_DISK_PROTOCOL EFI_RAM_DISK_PROTOCOL; 5 | 6 | typedef 7 | EFI_STATUS 8 | (EFIAPI *EFI_RAM_DISK_REGISTER_RAMDISK) ( 9 | IN UINT64 RamDiskBase, 10 | IN UINT64 RamDiskSize, 11 | IN EFI_GUID *RamDiskType, 12 | IN EFI_DEVICE_PATH *ParentDevicePath OPTIONAL, 13 | OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath 14 | ); 15 | 16 | /** 17 | Unregister a RAM disk specified by DevicePath. 18 | 19 | @param[in] DevicePath A pointer to the device path that describes a RAM 20 | Disk device. 21 | 22 | @retval EFI_SUCCESS The RAM disk is unregistered successfully. 23 | @retval EFI_INVALID_PARAMETER DevicePath is NULL. 24 | @retval EFI_UNSUPPORTED The device specified by DevicePath is not a 25 | valid ramdisk device path and not supported 26 | by the driver. 27 | @retval EFI_NOT_FOUND The RAM disk pointed by DevicePath doesn't 28 | exist. 29 | 30 | **/ 31 | typedef 32 | EFI_STATUS 33 | (EFIAPI *EFI_RAM_DISK_UNREGISTER_RAMDISK) ( 34 | IN EFI_DEVICE_PATH_PROTOCOL *DevicePath 35 | ); 36 | 37 | /// 38 | /// RAM Disk Protocol structure. 39 | /// 40 | struct _EFI_RAM_DISK_PROTOCOL { 41 | EFI_RAM_DISK_REGISTER_RAMDISK Register; 42 | EFI_RAM_DISK_UNREGISTER_RAMDISK Unregister; 43 | }; 44 | 45 | /// 46 | /// RAM Disk Protocol GUID variable. 47 | /// 48 | //EFI_GUID gEfiRamDiskProtocolGuid = { 0xab38a0df, 0x6873, 0x44a9, { 0x87, 0xe6, 0xd4, 0xeb, 0x56, 0x14, 0x84, 0x49 }}; 49 | #define EFI_RAMDISK_PROTOCOL_GUID EFI_GUID(0xab38a0df, 0x6873, 0x44a9, 0x87, 0xe6, 0xd4, 0xeb, 0x56, 0x14, 0x84, 0x49 ) 50 | #define EFI_VIRTUAL_DISK_GUID EFI_GUID( 0x77AB535A, 0x45FC, 0x624B, 0x55, 0x60, 0xF7, 0xB2, 0x81, 0xD1, 0xF9, 0x6E ) 51 | 52 | /// 53 | /// Media ram disk device path. 54 | /// 55 | #define MEDIA_RAM_DISK_DP 0x09 56 | 57 | /// 58 | /// Used to describe the ram disk device path. 59 | /// 60 | typedef struct { 61 | EFI_DEVICE_PATH_PROTOCOL Header; 62 | /// 63 | /// Starting Memory Address. 64 | /// 65 | UINT32 StartingAddr[2]; 66 | /// 67 | /// Ending Memory Address. 68 | /// 69 | UINT32 EndingAddr[2]; 70 | /// 71 | /// GUID that defines the type of the RAM Disk. 72 | /// 73 | EFI_GUID TypeGuid; 74 | /// 75 | /// RAM Diskinstance number, if supported. The default value is zero. 76 | /// 77 | UINT16 Instance; 78 | } MEDIA_RAM_DISK_DEVICE_PATH; 79 | 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /module/tpm.c: -------------------------------------------------------------------------------- 1 | /* UEFI TPM interface. 2 | * 3 | * This is a simplified TPM interface using the UEFI TCG protocols 4 | * and the "well known" software interrupt interface. 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "efiwrapper.h" 12 | #include "Tcg2Protocol.h" 13 | 14 | // why is this not available? 15 | extern struct tpm_chip *tpmm_chip_alloc(struct device *pdev, 16 | const struct tpm_class_ops *ops); 17 | extern int tpm_chip_register(struct tpm_chip *chip); 18 | 19 | // where is this defined? 20 | typedef struct __attribute__((__packed__)) { 21 | uint16_t tag; 22 | uint32_t paramSize; 23 | uint32_t responseCode; 24 | } TPM2_RESPONSE_HEADER; 25 | 26 | 27 | typedef struct { 28 | spinlock_t lock; 29 | EFI_TCG2_PROTOCOL * uefi_tpm; 30 | struct tpm_chip * chip; 31 | uint8_t recv_buf[4096]; 32 | } uefi_tpm_t; 33 | 34 | 35 | static struct platform_device * pdev; 36 | 37 | static int tpm_response_len(const uint8_t * buf) 38 | { 39 | /* size of the data received in the TPM2_RESPONSE_HEADER struct */ 40 | __be32 *native_size = (__force __be32 *) &buf[2]; 41 | return be32_to_cpu(*native_size); 42 | } 43 | 44 | 45 | static int uefi_tpm_send(struct tpm_chip * chip, u8 *buf, size_t len) 46 | { 47 | uefi_tpm_t * const priv = dev_get_drvdata(&chip->dev); 48 | unsigned long flags; 49 | int status; 50 | //printk("uefi tpm send %zu\n", len); 51 | 52 | uefi_memory_map_add(); 53 | 54 | spin_lock_irqsave(&priv->lock, flags); 55 | memset(priv->recv_buf, 0xCC, sizeof(priv->recv_buf)); 56 | 57 | status = priv->uefi_tpm->SubmitCommand( 58 | priv->uefi_tpm, 59 | len, 60 | buf, 61 | sizeof(priv->recv_buf), 62 | priv->recv_buf 63 | ); 64 | 65 | //print_hex_dump(KERN_INFO, "recv data", DUMP_PREFIX_OFFSET, 16, 1, priv->recv_buf, tpm_response_len(priv->recv_buf), true); 66 | 67 | spin_unlock_irqrestore(&priv->lock, flags); 68 | 69 | if (status != 0) 70 | { 71 | printk("uefi tpm error: %d\n", status); 72 | return -1; 73 | } 74 | 75 | 76 | //printk("uefi tpm send rc: %d\n", status); 77 | 78 | return status; 79 | } 80 | 81 | static int uefi_tpm_recv(struct tpm_chip * chip, u8 *buf, size_t len) 82 | { 83 | uefi_tpm_t * const priv = dev_get_drvdata(&chip->dev); 84 | unsigned long flags; 85 | uint32_t size; 86 | 87 | spin_lock_irqsave(&priv->lock, flags); 88 | 89 | size = tpm_response_len(priv->recv_buf); 90 | //printk("uefi tpm recv %u\n", size); 91 | 92 | if (size > len) 93 | { 94 | spin_unlock_irqrestore(&priv->lock, flags); 95 | return -EIO; 96 | } 97 | 98 | memcpy(buf, priv->recv_buf, size); 99 | spin_unlock_irqrestore(&priv->lock, flags); 100 | 101 | return size; 102 | } 103 | 104 | static u8 uefi_tpm_status(struct tpm_chip * chip) 105 | { 106 | return 0; // always ready 107 | } 108 | 109 | static int uefi_tpm_not_implemented(void) 110 | { 111 | printk("uefi tpm function not implemented\n"); 112 | return -1; 113 | } 114 | 115 | static const struct tpm_class_ops uefi_tpm_ops = { 116 | .flags = 0, 117 | .send = uefi_tpm_send, 118 | .recv = uefi_tpm_recv, 119 | .status = uefi_tpm_status, 120 | .cancel = (void*) uefi_tpm_not_implemented, 121 | .req_canceled = (void*) uefi_tpm_not_implemented, 122 | }; 123 | 124 | 125 | static struct seq_operations uefi_tpm2_seqops; 126 | static void * (*old_tpm2_bios_measurements_start)(struct seq_file *m, loff_t *pos); 127 | 128 | static void *uefi_tpm2_bios_measurements_start(struct seq_file *m, loff_t *pos) 129 | { 130 | struct tpm_chip * chip = m->private; 131 | struct tpm_bios_log * log = &chip->log; 132 | uefi_tpm_t * const priv = dev_get_drvdata(&chip->dev); 133 | const char * name = dev_name(&chip->dev); 134 | 135 | EFI_PHYSICAL_ADDRESS eventlog_phys, eventlog_end; 136 | BOOLEAN eventlog_truncated; 137 | size_t size, prev_size; 138 | int status; 139 | 140 | uefi_memory_map_add(); 141 | 142 | status = priv->uefi_tpm->GetEventLog( 143 | priv->uefi_tpm, 144 | EFI_TCG2_EVENT_LOG_FORMAT_TCG_2, 145 | &eventlog_phys, 146 | &eventlog_end, 147 | &eventlog_truncated 148 | ); 149 | if (status != 0) 150 | { 151 | printk("%s: get eventlog failed: %d\n", name, status); 152 | goto end; 153 | } 154 | 155 | // this is some massive onzin: the eventlog end is *NOT* 156 | // the size of the eventlog, but a pointer to the last entry 157 | // so finding the *actual* size requires parsing that entry 158 | // to figure out what is there. 159 | size = __calc_tpm2_event_size((void*) eventlog_end, (void*) eventlog_phys, false); 160 | size += eventlog_end - eventlog_phys; 161 | 162 | prev_size = log->bios_event_log_end - log->bios_event_log; 163 | 164 | if (size == prev_size) 165 | { 166 | // no change, so no new entries 167 | goto end; 168 | } 169 | 170 | printk("%s: eventlog=%llx end=%llx trunc=%d size=%zu prev=%zu\n", 171 | name, 172 | eventlog_phys, 173 | eventlog_end, 174 | eventlog_truncated, 175 | size, 176 | prev_size); 177 | 178 | // free the old copy 179 | kfree(log->bios_event_log); 180 | 181 | // we have the UEFI physical memory mapped 1:1, so 182 | // we can use physical address directly here for the copy 183 | log->bios_event_log = kmemdup((void*) eventlog_phys, size, GFP_KERNEL); 184 | log->bios_event_log_end = log->bios_event_log + size; 185 | 186 | // pass the call to the original method, regardless of what happens 187 | end: 188 | return old_tpm2_bios_measurements_start(m,pos); 189 | } 190 | 191 | 192 | static int uefi_tpm_eventlog_init(uefi_tpm_t * priv) 193 | { 194 | // the generic code has already installed the handlers 195 | struct tpm_chip * chip = priv->chip; 196 | 197 | // copy their handlers 198 | uefi_tpm2_seqops = *chip->bin_log_seqops.seqops; 199 | old_tpm2_bios_measurements_start = uefi_tpm2_seqops.start; 200 | uefi_tpm2_seqops.start = uefi_tpm2_bios_measurements_start; 201 | 202 | // and replace the pointer with ours 203 | chip->bin_log_seqops.seqops = &uefi_tpm2_seqops; 204 | 205 | return 0; 206 | } 207 | 208 | int uefi_tpm_init(void) 209 | { 210 | uefi_tpm_t * priv; 211 | struct tpm_chip * chip; 212 | EFI_TCG2_BOOT_SERVICE_CAPABILITY caps = { .Size = sizeof(caps) }; 213 | int status; 214 | 215 | pdev = platform_device_register_simple("tpm_uefi", -1, NULL, 0); 216 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 217 | priv->uefi_tpm = uefi_locate_and_handle_protocol(&EFI_TCG2_PROTOCOL_GUID); 218 | 219 | if (!priv->uefi_tpm) 220 | { 221 | printk("uefi tpm: not present?\n"); 222 | platform_device_unregister(pdev); 223 | return 0; 224 | } 225 | 226 | status = priv->uefi_tpm->GetCapability(priv->uefi_tpm, &caps); 227 | if (status != 0) 228 | printk("uefi tpm: get capability failed: %d\n", status); 229 | 230 | printk("uefi tpm: present=%d manufacturer=%08x\n", 231 | caps.TPMPresentFlag, 232 | caps.ManufacturerID 233 | ); 234 | 235 | spin_lock_init(&priv->lock); 236 | chip = priv->chip = tpmm_chip_alloc(&pdev->dev, &uefi_tpm_ops); 237 | dev_set_drvdata(&chip->dev, priv); 238 | 239 | uefi_tpm_eventlog_init(priv); 240 | 241 | // by setting the flag, tpm_read_log_efi() 242 | // will do all the work to setup our sysfs file 243 | chip->flags = 1 << 1; // TPM_CHIP_FLAG_TPM2 244 | 245 | tpm_chip_register(chip); 246 | 247 | // but now we need to hook the start method to ensure 248 | // that the addr and limit values are "live" since 249 | // new ones could be added. 250 | uefi_tpm_eventlog_init(priv); 251 | 252 | 253 | return 0; 254 | } 255 | -------------------------------------------------------------------------------- /tpm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DIR="${1:-./build/tpm-state}" 4 | PIDFILE="$DIR/swtpm.pid" 5 | VERBOSE= 6 | 7 | die() { echo "$*" >&2 ; exit 1 ; } 8 | warn() { echo "$*" >&2 ; } 9 | 10 | # Make the path absolute to work around https://github.com/stefanberger/swtpm/issues/671 11 | ABS_DIR="$(readlink -f "$DIR")" 12 | 13 | mkdir -p "$DIR" || die "$DIR: unable to create TPM state directory" 14 | 15 | if [ -r "$PIDFILE" ]; then 16 | PID="$(cat $PIDFILE)" 17 | warn "Killing old swtpm process $PID" 18 | kill "$PID" 2>&- 19 | fi 20 | 21 | if [ "$V" = 1 ]; then 22 | VERBOSE="--log level=20" 23 | fi 24 | 25 | if [ ! -r "$DIR/tpm2-00.permall" ]; then 26 | warn "Creating TPM state" 27 | swtpm_setup \ 28 | --tpm2 \ 29 | --createek \ 30 | --display \ 31 | --tpmstate "$DIR" \ 32 | --config /dev/null \ 33 | || die "$DIR: unable to setup TPM" 34 | fi 35 | 36 | # swtpm 0.7.1 --daemon fails with relative paths, so give it absolute ones 37 | # startup-clear ensures that the TPM is enabled and not locked 38 | swtpm socket \ 39 | --daemon \ 40 | --tpmstate dir="$ABS_DIR" \ 41 | --pid file="$ABS_DIR/swtpm.pid" \ 42 | --server type=unixio,path="$ABS_DIR/swtpm-sock" \ 43 | --ctrl type=unixio,path="$ABS_DIR/swtpm-sock.ctrl" \ 44 | --flags not-need-init,startup-clear \ 45 | --tpm2 \ 46 | $VERBOSE \ 47 | || die "$DIR: unable to start swtpm" 48 | 49 | 50 | #if [ ! -r "$DIR/ek.pem" ]; then 51 | warn "$DIR: reading EK public key" 52 | TPM2TOOLS_TCTI=swtpm:path="$DIR/swtpm-sock" \ 53 | tpm2 createek \ 54 | -c "$DIR/ek.ctx" \ 55 | -u "$DIR/ek.pem" \ 56 | -f pem \ 57 | || die "$DIR: unable to read EK" 58 | 59 | openssl rsa \ 60 | -noout \ 61 | -text \ 62 | -pubin \ 63 | -in build/tpm-state/ek.pem 64 | #fi 65 | 66 | 67 | echo "export TPM2TOOLS_TCTI=swtpm:path='$ABS_DIR/swtpm-sock'" 68 | --------------------------------------------------------------------------------