Slide 1

Slide 1 text

LLVM Backend Development for EFI Byte Code July 20, 2019 kernelvm15@Tokyo @retrage

Slide 2

Slide 2 text

TL;DR • LLVM backend development for EBC is hard. • Source code: • yabits/llvm:retrage/ebc • yabits/clang:retrage/ebc • yabits/lld:retrage/ebc • Blog post: • LLVMのEFI Byte Codeバックエンドを作る (ja) 1

Slide 3

Slide 3 text

UEFI has device drivers 2

Slide 4

Slide 4 text

Device Driver in Option ROM • Some PCIe devices have device drivers for UEFI in its Option ROM. 3

Slide 5

Slide 5 text

How to support multiple arch? • The device must have multiple device drivers for each supported architecture. 4

Slide 6

Slide 6 text

EBC solves the problem • ”platform- and processor-independent mechanisms for loading and executing EFI device drivers” 5

Slide 7

Slide 7 text

Available EBC Tools • EDK2[1] EBC VM reference implementation. • Intel C Compiler for EFI Byte Code[2] • fasmg-ebc: flat assembler based EBC assembler[3] • EbcDebugger: A standalone EBC Debugger[4] 6

Slide 8

Slide 8 text

Available EBC Tools • EDK2[1] EBC VM reference implementation. • EBC VM reference implementation[5]. • EBC assembly level debugger[6]. • You can run with QEMU+OVMF. • But UEFI does not provide memory protection. • Intel C Compiler for EFI Byte Code[2] • fasmg-ebc: flat assembler based EBC assembler[3] • EbcDebugger: A standalone EBC Debugger[4] 7

Slide 9

Slide 9 text

Available EBC Tools • EDK2[1] EBC VM reference implementation. • Intel C Compiler for EFI Byte Code[2] • Closed source, non-free ($955) • Please someone donate it to me. • fasmg-ebc: flat assembler based EBC assembler[3] • EbcDebugger: A standalone EBC Debugger[4] 8

Slide 10

Slide 10 text

Available EBC Tools • EDK2[1] EBC VM reference implementation. • Intel C Compiler for EFI Byte Code[2] • fasmg-ebc: flat assembler based EBC assembler[3] • It has a bug in encoding natural indexing. • EbcDebugger: A standalone EBC Debugger[4] • Not tried yet, but it seems re-packaging EDK2 EBC VM. 9

Slide 11

Slide 11 text

Available EBC Tools • EDK2[1] EBC VM reference implementation. • Intel C Compiler for EFI Byte Code[2] • fasmg-ebc: flat assembler based EBC assembler[3] • EbcDebugger: A standalone EBC Debugger[4] • => No easy-to-use VM or free compiler • What we need to create: • User space EBC VM to make debugging easy • ELVM backend for EBC 10

Slide 12

Slide 12 text

ebcvm: user space EBC VM 11 • Motivation: • Can I implement an EBC VM with only UEFI spec.? • For more details: • yabits/ebcvm • ebcvm: A Usermode EFI Byte Code Virtual Machine (ja) Registers Memory Decoder Executor Loader EFI Native Code Simple Debugger

Slide 13

Slide 13 text

ELVM backend for EBC • ELVM: EsoLangVM Compiler Infrastructure • It can emit little endian binaries. • EBC backend: • Add COFF binary format support. • Add EBC backend that generates raw binaries. • Test with ebcvm and passed all tests. • For more details: • retrage/elvm:retrage/ebc-v2 • ELVMのEFI Byte Codeバックエンドを作る (ja) 12

Slide 14

Slide 14 text

Demo ELVM EBC Backend test with ebcvm fizzbuzz_fast.c.eir.ebc on QEMU+OVMF 13

Slide 15

Slide 15 text

ELVM is great, but... • Generated code is not optimized. • The binaries tend to be fat. • ELVM does not have linking stage. • Build means compiling and linking. • Only one file can be built at once. • => Using ELVM backend is not practical. • => Develop LLVM backend for EBC: • LLVM Core, Clang, LLD 14

Slide 16

Slide 16 text

Goal: Compile and Link this code 15 #include "efi.h" EFI_STATUS efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) { CHAR16 *String = L"Hello, EBC!\n"; asm("movnw %0, @%0 (5,24)\n" // SystemTable->ConOut "pushn %1\n" // String "pushn %0\n" // SystemTable "call32exa @%0 (1,0)\n" // ConOut->OutputString "movqw r0, r0 (2,0)\n" ::"r"(SystemTable), "r"(String):); return EFI_SUCCESS; }

Slide 17

Slide 17 text

MCLayer and CodeGen • MCLayer: • .s -> MCInst -> .o • CodeGen: • LLVM IR -> SelectionDAG -> MachineInstr -> MCInst ->.o 16

Slide 18

Slide 18 text

MCLayer and CodeGen • MCLayer: • .s -> MCInst -> .o • CodeGen: • LLVM IR -> SelectionDAG -> MachineInstr -> MCInst ->.o • Approach: start with MCLayer • Same approach with RISC-V backend. • https://speakerdeck.com/asb/llvm-backend- development-by-example-risc-v?slide=4 17

Slide 19

Slide 19 text

MCLayer: TODO list • Add Triple and EBC COFF defines. • Describe register and instruction formats. • Define instructions and operand types. • Add MCTargetDesc/* and InstPrinter/*. • Add AsmParser. • Add Disassembler. • Add fixups support. • Write tests. 18

Slide 20

Slide 20 text

MCLayer: TODO list • Add Triple and EBC COFF defines. • Describe register and instruction formats. • Define instructions and operand types. • Add MCTargetDesc/* and InstPrinter/*. • Add AsmParser. • Add Disassembler. • Add fixups support. • Write tests. 19

Slide 21

Slide 21 text

Natural Indexing: Example • Following arguments are passed at the entry point: • ImageHandle • SystemTable 20 typedef EFI_STATUS (EFIAPI *EFI_IMAGE_ENTRY_POINT) ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable );

Slide 22

Slide 22 text

Natural Indexing: Example • In EBC Calling Convention, arguments are passed via stack in reverse order. 21 typedef EFI_STATUS (EFIAPI *EFI_IMAGE_ENTRY_POINT) ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ); ImageHandle SystemTable Low High Stack top Stack pointer

Slide 23

Slide 23 text

Natural Indexing: Example • If you want to get SystemTable, you have to know the size of ImageHandle. • However, the size of ImageHandle is host- dependent (as the type is void * in host) • On 32-bit host: 4-byte • On 64-bit host: 8-byte 22 ImageHandle SystemTable SP On 32-bit host 0 31 ImageHandle SystemTable SP 0 63 On 64-bit host

Slide 24

Slide 24 text

Natural Indexing • Natural Indexing: (, ) • A set of natural unit and constant unit • = ∗ ∗ + • Note: ( ∗) is host-dependent • For the previous case: (+1,0) • On 32-bit host: (1 ∗ 4 + 0) = 4 • On 64-bit host: (1 ∗ 8 + 0) = 8 • On LLVM EBC backend: • Both units are represented as immediate. • Implement custom natural index encoder/decoder. 23

Slide 25

Slide 25 text

Instruction defines: Easy mode • LLVM has TableGen domain specific language. • TableGen has C++ like class. • Instructions are described by instantiating class. • Take a look at the simple example: STORESP 24

Slide 26

Slide 26 text

Instruction defines: STORESP 25 def STORESP : Instruction { field bits<16> Inst; dag OutOperandList = (outs GPR:$op1); dag InOperandList = (ins DR:$op2); let Namespace = "EBC"; let AsmString = "storesp\t$op1, $op2"; let Size = 2; let Inst{14-12} = op2; let Inst{10-8} = op1; let Inst{5-0} = 0b101010; }

Slide 27

Slide 27 text

Instruction defines: STORESP 26 def STORESP : Instruction { field bits<16> Inst; dag OutOperandList = (outs GPR:$op1); dag InOperandList = (ins DR:$op2); let Namespace = "EBC"; let AsmString = "storesp\t$op1, $op2"; let Size = 2; let Inst{14-12} = op2; let Inst{10-8} = op1; let Inst{5-0} = 0b101010; } • Input/Output in DAG

Slide 28

Slide 28 text

Instruction defines: STORESP 27 def STORESP : Instruction { field bits<16> Inst; dag OutOperandList = (outs GPR:$op1); dag InOperandList = (ins DR:$op2); let Namespace = "EBC"; let AsmString = "storesp\t$op1, $op2"; let Size = 2; let Inst{14-12} = op2; let Inst{10-8} = op1; let Inst{5-0} = 0b101010; } • Assembly format

Slide 29

Slide 29 text

Instruction defines: STORESP 28 def STORESP : Instruction { field bits<16> Inst; dag OutOperandList = (outs GPR:$op1); dag InOperandList = (ins DR:$op2); let Namespace = "EBC"; let AsmString = "storesp\t$op1, $op2"; let Size = 2; let Inst{14-12} = op2; let Inst{10-8} = op1; let Inst{5-0} = 0b101010; } • Instruction Encoding

Slide 30

Slide 30 text

Instruction defines: Hard mode • Most of EBC instructions have optional operands. • Example: ALU operations • 32 64 @ @ , @ A {16|16} • 32-bit/64-bit operation • Operand 1 Direct/Indirect • Operand 2 Direct/Indirect • Operand 2 Optional Index/Immediate • 16 ways of combinations per instruction. • => Implement all using multiclass. 29

Slide 31

Slide 31 text

EBCALU multiclass 30 multiclass EBCALU opcode, string opcodestr> { foreach hasImmIdx = [0b0, 0b1] in { foreach is64Bit = [0b0, 0b1] in { foreach Op1Indirect = [0b0, 0b1] in { foreach Op2Indirect = [0b0, 0b1] in def !if(is64Bit, "64", "32") # !if(Op1Indirect, "Op1I", "Op1D") # !if(Op2Indirect, "Op2I", "Op2D") # !cond(!eq(hasImmIdx, 0) : "", !eq(!and(hasImmIdx, Op2Indirect), 0) : "Imm", !eq(!and(hasImmIdx, Op2Indirect), 1) : "Idx") : EBCALUBase; } } } }

Slide 32

Slide 32 text

EBCALUBase class 31 class EBCALUBase opcode, bit hasImmIdx, bit is64Bit, bit Op1Indirect, bit Op2Indirect, dag outs, dag ins, dag immins, dag idxins, string opcodestr, string op1str, string op2str, string immstr, string idxstr> : EBCInst2Op { bits<3> dst; let CodeSize = !if(hasImmIdx, 4, 2); let mayLoad = !if(Op2Indirect, 1, 0); let mayStore = !if(Op1Indirect, 1, 0); }

Slide 33

Slide 33 text

MCLayer and CodeGen • MCLayer: • .s -> MCInst -> .o • CodeGen: • LLVM IR -> SelectionDAG -> MachineInstr -> MCInst ->.o 32

Slide 34

Slide 34 text

MCLayer and CodeGen • MCLayer: • .s -> MCInst -> .o • CodeGen: • LLVM IR -> SelectionDAG -> MachineInstr -> MCInst ->.o • Just do it. 33

Slide 35

Slide 35 text

CodeGen: TODO list • ALU operations • Materializing constants • Memory operation • Global address operation • Conditional branches • Function calls • SELECT/SELECT_CC • FrameIndex lowering • Prologue/Epilogue insertion • dynamic_stackalloc, stacksave, stackrestore • Inline assembly • And more! 34

Slide 36

Slide 36 text

EBC binary must be relocatable • This means that all addresses must be relative. • MOVREL Operation: • 1 <= [ + ℎ + ] 35 MOVREL Instr Instruction Pointer Data0 .text section Data1 .data section Op1 Register= Data1 Offset

Slide 37

Slide 37 text

Missing instruction in EBC • What about just calculate the address from offset? • Something like: • 1 < = + ℎ + 36 ?? Instr Instruction Pointer Data0 .text section Data1 .data section Op1 Register = Absolute Address of Data 1 Offset

Slide 38

Slide 38 text

Missing instruction in EBC • 1 < = + ℎ + • EBC does not have such an instruction. • We can use STORESP+MOVI+ADD set to achieve it. 37 STORESP MOVI .text section Data1 .data section ADD = 0 RST Offset

Slide 39

Slide 39 text

Missing instruction in EBC • 1 < = + ℎ + • EBC does not have such an instruction. • We can use STORESP+MOVI+ADD set to achieve it. 38 STORESP MOVI .text section Data1 .data section ADD = 1 RS@ U = RS@ Offset

Slide 40

Slide 40 text

Missing instruction in EBC • 1 < = + ℎ + • EBC does not have such an instruction. • We can use STORESP+MOVI+ADD set to achieve it. 39 STORESP MOVI .text section Data1 .data section ADD = 2 RSA U = RS@ V = Offset

Slide 41

Slide 41 text

Missing instruction in EBC • 1 < = + ℎ + • EBC does not have such an instruction. • We can use STORESP+MOVI+ADD set to achieve it. 40 STORESP MOVI .text section Data1 .data section ADD = 3 U = RS@ + V = Offset

Slide 42

Slide 42 text

Clang and LLD • Clang: • Add Triple, target specific driver, and TargetInfo • Most of the code borrowed from MSVC driver. • LLD: • Add SectionChunk::applyRelEBC() • No DLL support • No PDB support 41

Slide 43

Slide 43 text

Demo Compile and Link the goal source code Execute the generated EBC binary in QEMU+OVMF 42

Slide 44

Slide 44 text

Sad news: EBC is now optional 43 • 2.8: Remove the EBC support requirement

Slide 45

Slide 45 text

Conclusion • EFI Byte Code (EBC) is an architecture-independent mechanism to execute UEFI device driver. • There is only a few tools for EBC. • I developed ebcvm and ELVM EBC backend. • For more practical use, I am developing LLVM backend for EBC. • EBC design is insane. • Complicated Instruction with Optional Operand. • Lack of necessary instruction. 44

Slide 46

Slide 46 text

References • [1] https://github.com/tianocore/edk2 • [2] https://software.intel.com/en-us/articles/intel-c- compiler-for-efi-byte-code-purchase • [3] https://github.com/pbatard/fasmg-ebc • [4] https://github.com/pbatard/EbcDebugger • [5] https://github.com/tianocore/edk2/blob/master/Mde ModulePkg/Universal/EbcDxe/EbcExecute.c • [6] https://github.com/tianocore/edk2/tree/master/Mde ModulePkg/Universal/EbcDxe/EbcDebugger • [7] 45

Slide 47

Slide 47 text

Appendix 46

Slide 48

Slide 48 text

Decoding hell 47

Slide 49

Slide 49 text

Decoding hell II 48 • Why optional index come before mandatory immediate?

Slide 50

Slide 50 text

Unclear spec. • @ @ , Immed • Restriction: • “Specifying an index value with Operand 1 direct results in an instruction encoding exception”. • @ @ , @ A {} • Spec does not explicitly imply that index value with Operand 2 direct is allowed. • It is actually allowed according to EDK2 VM. 49

Slide 51

Slide 51 text

Fixup kinds • fixups in EBC is {16,32,64}-bit IP-relative • However, the offset is vary: • Most of EBC instruction: +2 • CALL32: -4 • JMP64: -8 • Is there any smart way to specify the offset? 50