├── .gitignore ├── bin ├── setup-qemu.sh ├── iommu.sh └── build.sh ├── roms ├── quadro-1200m.rom └── gtx-1050ti-maxq.rom ├── README.md ├── docker-build ├── Dockerfile └── files │ ├── prepare-rom-patch.sh │ ├── compile-ovmf.sh │ ├── QemuFwCfgAcpi.c.patch │ ├── ssdt.asl │ └── IntelIGD.patch ├── .travis.yml └── qemu └── windows_server_2019_gvt_nv.xml /.gitignore: -------------------------------------------------------------------------------- 1 | build/* -------------------------------------------------------------------------------- /bin/setup-qemu.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | virsh define --validate qemu/windows_server_2019_gvt_nv.xml 4 | -------------------------------------------------------------------------------- /roms/quadro-1200m.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitthief/ovmf-with-vbios-patch/HEAD/roms/quadro-1200m.rom -------------------------------------------------------------------------------- /roms/gtx-1050ti-maxq.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitthief/ovmf-with-vbios-patch/HEAD/roms/gtx-1050ti-maxq.rom -------------------------------------------------------------------------------- /bin/iommu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | shopt -s nullglob 3 | for d in /sys/kernel/iommu_groups/*/devices/*; do 4 | n=${d#*/iommu_groups/*}; n=${n%%/*} 5 | printf 'IOMMU Group %s ' "$n" 6 | lspci -nns "${d##*/}" 7 | done; 8 | -------------------------------------------------------------------------------- /bin/build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | mkdir -p build 4 | docker build -t ovmf-vbios-patch docker-build 5 | docker run --rm -v "$PWD/build:/build" -v "$PWD/roms:/roms" ovmf-vbios-patch /ovmf/compile-ovmf.sh gtx-1050ti-maxq.rom 6 | tar -czf build/ovmf-vbios-patched.tgz build/OVMF* 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ovmf-with-vbios-patch 2 | 3 | Based on @marcosscriven's initial work. 4 | 5 | Automates the build of a patched OVMF image to be used with QEMU for NVIDIA GPU PCI/VFIO Passthrough. 6 | 7 | Additionally, adds support for Intel GVT-g / RAMFB, which finally allows emulating a hybrid GPU setup (Optimus). 8 | 9 | Tested and confirmed to work on Dell XPS 15 (9570). 10 | -------------------------------------------------------------------------------- /docker-build/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:bionic 2 | LABEL maintainer="bitthief@pm.me" 3 | 4 | RUN apt-get -qq -y update && \ 5 | apt-get -qq -y upgrade && \ 6 | apt-get -qq -y install -y wget vim build-essential gcc-5 g++-5 libgcc-5-dev git python2.7 iasl nasm subversion libwww-perl uuid-dev dos2unix 7 | RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 60 --slave /usr/bin/g++ g++ /usr/bin/g++-7 && \ 8 | update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 60 --slave /usr/bin/g++ g++ /usr/bin/g++-5 && \ 9 | echo 1 | update-alternatives --config gcc 10 | RUN git clone --recursive https://github.com/tianocore/edk2.git 11 | COPY files/ /ovmf 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | services: 3 | - docker 4 | script: 5 | - "./bin/build.sh" 6 | deploy: 7 | provider: releases 8 | api_key: 9 | secure: Pojtmh/34hFfhVQTGQwa2lSGW+KuUSbKkt+1Zh0ySPAkw3nbfHMV6leQDC/laCtWVLbBPlv2Q9QQ3giKa+ygmNMwN9toZjQrkUtHwnCPTOEzSRMSA1OGuQex/QRivcSmqK1bbVx2K4bHPZRfC0XbGK30DMOA6eBiVhssFf06PwgfmuwVZK501aHGC4bWJI9KAGgRDjlkEzJkuqh7A4Ju0ZAr8Jl06yZ276nnRPeoxa+ekwcx4h2Sf+6xLSkDIr22GwT+BKcA+cJYMvHZqQfMwY8y0vrC9ka+4GgCEvGRkMpH/zic3guUC7/w6WDjjM0/KW0sy2aFC8dO+hCQ7sTAWa+raSXeECrUjXVOn4n7oKxfTMeR3z45NVh4OjplmbHd1INSg8LxmQ/7j/CLpuABBEvZduqO8PSG/IbM5++9tAzGevl2amGRrSLwhNTLnCh78CSSQZ6fWjRljFxC5Q6ij2Fs8uVuYmFXY3ClAQCEK2yPK8AjkLQfb61ByrERlhla1pwhDvfo8oSYqFgBDxyWdrio8Ab0BmEU4ZkmO1gHcvHn6dH4bunaIZcnqNquXUtlQU0f9rToNWsDFELvz6KVwJ+JCIKrzOtz+XfCoSPkp61PQGTHYciTpfDOdcjW5mKwzrHns7s0q8mUt3agcFBC38NR4A0b/OzYgiZrWFu6BgE= 10 | file: build/ovmf-vbios-patched.tgz 11 | skip_cleanup: true 12 | on: 13 | tags: true -------------------------------------------------------------------------------- /docker-build/files/prepare-rom-patch.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # This will take a regular ROM file and create a vrom.h and vrom_table.h for it 4 | function prepareRomPatch() { 5 | rom_file=$1 6 | 7 | echo "Preparing patch for $rom_file" 8 | 9 | # Work in the /patches dir 10 | mkdir -p /patches 11 | pushd /patches 12 | 13 | # Make a header file from the binary rom 14 | cp "/roms/${rom_file}" vBIOS.bin 15 | xxd -i vBIOS.bin vrom.h 16 | sed -i 's/vBIOS_bin/VROM_BIN/g; s/_len/_LEN/g' vrom.h 17 | 18 | # Grab its length and update it in the ssdt 19 | cp /ovmf/ssdt.asl . 20 | rom_len=$(grep VROM_BIN_LEN vrom.h | cut -d' ' -f5 | sed 's/;//g') 21 | sed -i "s/103936/$rom_len/g" ssdt.asl 22 | iasl -f ssdt.asl 23 | 24 | # Create a vrom table 25 | xxd -c1 Ssdt.aml | tail -n +37 | cut -f2 -d' ' | paste -sd' ' | sed 's/ //g' | xxd -r -p > vrom_table.aml 26 | xxd -i vrom_table.aml | sed 's/vrom_table_aml/vrom_table/g' > vrom_table.h 27 | 28 | popd 29 | } 30 | 31 | prepareRomPatch $1 -------------------------------------------------------------------------------- /docker-build/files/compile-ovmf.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # This build is based on information gather from: 4 | # 5 | # https://www.reddit.com/r/VFIO/comments/8gv60l/current_state_of_optimus_muxless_laptop_gpu/ 6 | # https://github.com/jscinoz/optimus-vfio-docs/issues/2#issuecomment-380335101 7 | # https://gist.github.com/Ashymad/2c8192519492dec262b344deb68fed44 8 | # 9 | 10 | rom_file=$1 11 | echo "Building OVMF with a VBIOS for $rom_file" 12 | 13 | # Prepare env 14 | export SRC_DIR="/edk2" 15 | export PATH="${SRC_DIR}/bin:${PATH}" 16 | export EDK_TOOLS_PATH="${SRC_DIR}/BaseTools" 17 | 18 | # Prepare for build 19 | cd ${SRC_DIR} 20 | mkdir -p bin 21 | ln -sf /usr/bin/python2.7 bin/python 22 | git pull 23 | #git checkout vUDK2018 24 | git checkout edk2-stable201905 25 | git pull --recurse-submodules 26 | git submodule update --recursive 27 | 28 | # Build Basetools 29 | make -C BaseTools 30 | 31 | # NVIDIA VBIOS Patches 32 | /ovmf/prepare-rom-patch.sh $rom_file 33 | cp /patches/vrom.h OvmfPkg/AcpiPlatformDxe/ 34 | cp /patches/vrom_table.h OvmfPkg/AcpiPlatformDxe/ 35 | 36 | dos2unix OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c 37 | patch -p1 < /ovmf/QemuFwCfgAcpi.c.patch 38 | unix2dos OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c 39 | 40 | # Intel GVT-g Patches 41 | patch -p1 < /ovmf/IntelIGD.patch 42 | 43 | # Disable PIE 44 | sed -r -i \ 45 | -e 's/^BUILD_CFLAGS[[:space:]]*=(.*[a-zA-Z0-9])?/\0 -fPIC/' \ 46 | BaseTools/Source/C/Makefiles/header.makefile || exit 1 47 | sed -i '/^build -p/i echo $TARGET_TOOLS > target_tools_var' \ 48 | OvmfPkg/build.sh || exit 1 49 | 50 | # This build system is impressively complicated, needless to say 51 | # it does things that get confused by PIE being enabled by default. 52 | # Add -nopie to a few strategic places... :) 53 | sed -r -i \ 54 | -e 's/^DEFINE GCC_ALL_CC_FLAGS[[:space:]]*=(.*[a-zA-Z0-9])?/\0 -fno-pie/' \ 55 | -e 's/^DEFINE GCC44_ALL_CC_FLAGS[[:space:]]*=(.*[a-zA-Z0-9])?/\0 -fno-pie/' \ 56 | BaseTools/Conf/tools_def.template || exit 1 57 | sed -r -i \ 58 | -e 's/^BUILD_CFLAGS[[:space:]]*=(.*[a-zA-Z0-9])?/\0 -fno-pie/' \ 59 | -e 's/^BUILD_LFLAGS[[:space:]]*=(.*[a-zA-Z0-9])?/\0 -no-pie/' \ 60 | BaseTools/Source/C/Makefiles/header.makefile || exit 1 61 | 62 | # Build OVMF 63 | . edksetup.sh BaseTools 64 | ./BaseTools/BinWrappers/PosixLike/build -t GCC5 -a IA32 -a X64 -p OvmfPkg/OvmfPkgIa32X64.dsc -n $(nproc) -b RELEASE -D FD_SIZE_2MB -D SMM_REQUIRE -D SECURE_BOOT_ENABLE -D HTTP_BOOT_ENABLE -D TLS_ENABLE -D NETWORK_IP6_ENABLE -D TPM2_ENABLE -D TPM2_CONFIG_ENABLE -D EXCLUDE_SHELL_FROM_FD 65 | 66 | # Copy to host build dir 67 | cp ${SRC_DIR}/Build/Ovmf3264/RELEASE_GCC5/FV/OVMF_CODE.fd /build 68 | cp ${SRC_DIR}/Build/Ovmf3264/RELEASE_GCC5/FV/OVMF_VARS.fd /build 69 | -------------------------------------------------------------------------------- /docker-build/files/QemuFwCfgAcpi.c.patch: -------------------------------------------------------------------------------- 1 | diff --git a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c 2 | index a0b1cfd2be..660007a74e 100644 3 | --- a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c 4 | +++ b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c 5 | @@ -24,6 +24,8 @@ 6 | #include 7 | #include 8 | #include 9 | +#include "vrom.h" 10 | +#include "vrom_table.h" 11 | 12 | 13 | // 14 | @@ -1107,6 +1110,77 @@ InstallQemuFwCfgTables ( 15 | } 16 | } 17 | } 18 | + 19 | + // modification: add additional SSDT 20 | + UINT32 VromSize = 256*1024; 21 | + UINT8* FwData = AllocateRuntimePool(VromSize); // 256*1024 = 256 KB = size of VROM image 22 | + 23 | + // copy VROM to FwData 24 | + CopyMem (FwData, VROM_BIN, VROM_BIN_LEN); 25 | + 26 | + // header of SSDT table: DefinitionBlock ("Ssdt.aml", "SSDT", 1, "REDHAT", "OVMF ", 1) 27 | + unsigned char Ssdt_header[] = { 28 | + 0x53, 0x53, 0x44, 0x54, 0x24, 0x00, 0x00, 0x00, 0x01, 0x86, 0x52, 0x45, 29 | + 0x44, 0x48, 0x41, 0x54, 0x4f, 0x56, 0x4d, 0x46, 0x20, 0x20, 0x20, 0x20, 30 | + 0x01, 0x00, 0x00, 0x00, 0x49, 0x4e, 0x54, 0x4c, 0x31, 0x08, 0x16, 0x20 31 | + }; 32 | + 33 | + // byte 4-7: length header + table in little endian (so equal to SsdtSize) 34 | + // byte 8: version complicance number: nothing needs to change 35 | + // byte 9: set such that when all bytes are added modulo 256 the sum equals 0 36 | + // calculate checksum: already implemented as CalculateCheckSum8(offset, length) 37 | + 38 | + unsigned int Ssdt_header_len = 36; 39 | + 40 | + UINTN SsdtSize; 41 | + UINT8 *Ssdt; 42 | + 43 | + SsdtSize = Ssdt_header_len + 17 + vrom_table_len; 44 | + Ssdt = AllocatePool (SsdtSize); 45 | + 46 | + UINT8 *SsdtPtr = Ssdt; 47 | + 48 | + // copy header to Ssdt table 49 | + CopyMem (SsdtPtr, Ssdt_header, Ssdt_header_len); 50 | + SsdtPtr += Ssdt_header_len; 51 | + 52 | + // build "OperationRegion(FWDT, SystemMemory, 0x12345678, 0x87654321)" 53 | + // 54 | + *(SsdtPtr++) = 0x5B; // ExtOpPrefix 55 | + *(SsdtPtr++) = 0x80; // OpRegionOp 56 | + *(SsdtPtr++) = 'V'; 57 | + *(SsdtPtr++) = 'B'; 58 | + *(SsdtPtr++) = 'O'; 59 | + *(SsdtPtr++) = 'R'; 60 | + *(SsdtPtr++) = 0x00; // SystemMemory 61 | + *(SsdtPtr++) = 0x0C; // DWordPrefix 62 | + 63 | + // 64 | + // no virtual addressing yet, take the four least significant bytes 65 | + // 66 | + CopyMem(SsdtPtr, &FwData, 4); 67 | + SsdtPtr += 4; 68 | + 69 | + *(SsdtPtr++) = 0x0C; // DWordPrefix 70 | + 71 | + *(UINT32*) SsdtPtr = VromSize; 72 | + SsdtPtr += 4; 73 | + 74 | + CopyMem (SsdtPtr, vrom_table, vrom_table_len); 75 | + 76 | + // set the correct size in the header 77 | + UINT32* size_ptr = (UINT32*) &Ssdt[4]; 78 | + *size_ptr = SsdtSize; 79 | + 80 | + // set byte 9 of header so the checksum equals 0 81 | + Ssdt[9] = 0; 82 | + UINT32 checksum = CalculateCheckSum8(Ssdt, SsdtSize); 83 | + Ssdt[9] = (256 - checksum) % 256; 84 | + 85 | + Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol, 86 | + (VOID *) Ssdt, SsdtSize, 87 | + &InstalledKey[Installed]); 88 | + ++Installed; 89 | 90 | // 91 | // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3 92 | -------------------------------------------------------------------------------- /docker-build/files/ssdt.asl: -------------------------------------------------------------------------------- 1 | DefinitionBlock ("Ssdt.aml", "SSDT", 1, "REDHAT", "OVMF2 ", 1) { 2 | /* copied from jscinoz */ 3 | 4 | External(\_SB.PCI0.FWCF, DeviceObj) 5 | External(\_SB.PCI0.FWCF._CRS, BuffObj) 6 | 7 | Device (\_SB.PCI0.PEG0) { 8 | Name (_ADR, 0x00010000) 9 | } 10 | 11 | Device (\_SB.PCI0.PEG0.PEGP) { 12 | Name (_ADR, Zero) 13 | 14 | /* ROML and ROMB not necessary here */ 15 | CreateByteField(\_SB.PCI0.FWCF._CRS, 0, ID) 16 | CreateWordField(\_SB.PCI0.FWCF._CRS, 2, BMIN) 17 | CreateWordField(\_SB.PCI0.FWCF._CRS, 4, BMAX) 18 | CreateByteField(\_SB.PCI0.FWCF._CRS, 7, LEN) 19 | 20 | // XXX: Unsure why, but these must be two separate regions, even though 21 | // FWD is entirely contained within FWC 22 | OperationRegion(FWC, SystemIO, BMIN, 2) 23 | OperationRegion(FWD, SystemIO, BMIN + 1, 1) 24 | Field(FWC, WordAcc, Lock, Preserve) { 25 | CTRL, 16 26 | } 27 | 28 | // Yes, DATA overlaps CTRL. This is not a mistake 29 | Field(FWD, ByteAcc, NoLock, Preserve) { 30 | DATA, 8 31 | } 32 | } 33 | 34 | /* define the ROM call */ 35 | Scope (\_SB.PCI0.PEG0.PEGP) 36 | { 37 | Name (RVBS, 103936) // size of ROM in bytes 38 | 39 | External (\VBOR, OpRegionObj) 40 | Field (\VBOR, DWordAcc, Lock, Preserve) 41 | { 42 | VBS1, 262144, // length of segment in bits 43 | VBS2, 262144, 44 | VBS3, 262144, 45 | VBS4, 262144, 46 | VBS5, 262144, 47 | VBS6, 262144, 48 | VBS7, 262144, 49 | VBS8, 262144 50 | } 51 | 52 | Method (_ROM, 2, Serialized) // _ROM: Read-Only Memory 53 | { 54 | Local0 = Arg0 // offset in bytes 55 | Local1 = Arg1 // length in bytes 56 | 57 | // create a buffer to store the result 58 | // initialize to empty buffer 59 | Name (VROM, Buffer (Local1) 60 | { 61 | 0x00 62 | }) 63 | 64 | // length > 4k 65 | If (Local1 > 0x1000) 66 | { 67 | Local1 = 0x1000 68 | } 69 | 70 | // offset > size of ROM in bytes 71 | If (Local0 > RVBS) 72 | { 73 | Return (VROM) /* \_SB_.PCI0.PEG0.PEGP._ROM.VROM */ 74 | } 75 | 76 | // end position 77 | Local2 = (Arg0 + Arg1) 78 | // end position > ROM size 79 | If (Local2 > RVBS) 80 | { 81 | // set length to image length - start offset 82 | Local1 = (RVBS - Local0) 83 | } 84 | 85 | // 0x8000: segment size in bytes 86 | // Local3: remainder, Local4: in which segment 87 | Divide (Local0, 0x8000, Local3, Local4) 88 | 89 | // set Local5 to the right segment 90 | If (Local4 == 0) 91 | { 92 | Local5 = \_SB.PCI0.PEG0.PEGP.VBS1 93 | } 94 | ElseIf (Local4 == 1) 95 | { 96 | Local5 = \_SB.PCI0.PEG0.PEGP.VBS2 97 | } 98 | ElseIf (Local4 == 2) 99 | { 100 | Local5 = \_SB.PCI0.PEG0.PEGP.VBS3 101 | } 102 | ElseIf (Local4 == 3) 103 | { 104 | Local5 = \_SB.PCI0.PEG0.PEGP.VBS4 105 | } 106 | ElseIf (Local4 == 4) 107 | { 108 | Local5 = \_SB.PCI0.PEG0.PEGP.VBS5 109 | } 110 | ElseIf (Local4 == 5) 111 | { 112 | Local5 = \_SB.PCI0.PEG0.PEGP.VBS6 113 | } 114 | ElseIf (Local4 == 6) 115 | { 116 | Local5 = \_SB.PCI0.PEG0.PEGP.VBS7 117 | } 118 | ElseIf (Local4 == 7) 119 | { 120 | Local5 = \_SB.PCI0.PEG0.PEGP.VBS8 121 | } 122 | 123 | // extract from the segment starting from offset Local3 of size Local1 124 | // from Local 5 and store into VROM 125 | Mid (Local5, Local3, Local1, VROM) 126 | Return (VROM) /* \_SB_.PCI0.PEG0.PEGP._ROM.VROM */ 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /qemu/windows_server_2019_gvt_nv.xml: -------------------------------------------------------------------------------- 1 | 2 | Windows_Server_2019 3 | feb18f68-865a-4ed3-a1ea-b917a1d016ef 4 | 5 | 6 | 7 | 8 | 9 | 8388608 10 | 8388608 11 | 12 | 13 | 14 | 4 15 | 4 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | hvm 24 | /usr/share/OVMF/OVMF_CODE.fd 25 | /var/lib/libvirt/qemu/nvram/Windows_Server_2019_VARS.fd 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | destroy 56 | restart 57 | destroy 58 | 59 | 60 | 61 | 62 | 63 | /usr/bin/qemu-system-x86_64 64 | 65 | 66 | 67 | 68 | 69 |
70 | 71 | 72 |
73 | 74 | 75 |
76 | 77 | 78 |
79 | 80 | 81 | 82 | 83 | 84 |
85 | 86 | 87 | 88 | 89 |
90 | 91 | 92 | 93 | 94 |
95 | 96 | 97 | 98 | 99 |
100 | 101 | 102 | 103 | 104 |
105 | 106 | 107 | 108 | 109 |
110 | 111 | 112 | 113 |
114 | 115 | 116 | 117 | 118 |
119 | 120 | 121 | 122 | 123 |
124 | 125 | 126 |
127 | 128 | 129 | 130 | 131 | 132 | 133 |
134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 |
149 | 150 | 151 | 152 |
153 | 154 | 155 | 156 |
157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 |
171 | 172 | 175 | 176 | 177 |
178 | 179 | 180 |
181 | 182 | 183 | 184 |
185 | 186 |
187 | 188 | 189 |
190 | 191 | 192 |
193 | 194 | 195 | /dev/urandom 196 |
197 | 198 | 199 | 200 | 32 201 |
202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | -------------------------------------------------------------------------------- /docker-build/files/IntelIGD.patch: -------------------------------------------------------------------------------- 1 | From ba07eef98ec49068d6453aba2aed73f6e7d7f600 Mon Sep 17 00:00:00 2001 2 | From: Hou Qiming 3 | Date: Tue, 26 Feb 2019 21:13:04 +0800 4 | Subject: [PATCH] proper IgdAssignment debug, ramfb resolution update support 5 | 6 | --- 7 | OvmfPkg/IgdAssignmentDxe/IgdAssignment.c | 639 ++++++++++++++++++ 8 | OvmfPkg/IgdAssignmentDxe/IgdAssignment.inf | 44 ++ 9 | .../Include/IndustryStandard/AssignedIgd.h | 50 ++ 10 | OvmfPkg/OvmfPkgIa32.dsc | 1 + 11 | OvmfPkg/OvmfPkgIa32.fdf | 1 + 12 | OvmfPkg/OvmfPkgIa32X64.dsc | 1 + 13 | OvmfPkg/OvmfPkgIa32X64.fdf | 1 + 14 | OvmfPkg/OvmfPkgX64.dsc | 1 + 15 | OvmfPkg/OvmfPkgX64.fdf | 1 + 16 | OvmfPkg/QemuRamfbDxe/QemuRamfb.c | 18 +- 17 | 10 files changed, 753 insertions(+), 4 deletions(-) 18 | create mode 100644 OvmfPkg/IgdAssignmentDxe/IgdAssignment.c 19 | create mode 100644 OvmfPkg/IgdAssignmentDxe/IgdAssignment.inf 20 | create mode 100644 OvmfPkg/Include/IndustryStandard/AssignedIgd.h 21 | 22 | diff --git a/OvmfPkg/IgdAssignmentDxe/IgdAssignment.c b/OvmfPkg/IgdAssignmentDxe/IgdAssignment.c 23 | new file mode 100644 24 | index 00000000000..1f05a334236 25 | --- /dev/null 26 | +++ b/OvmfPkg/IgdAssignmentDxe/IgdAssignment.c 27 | @@ -0,0 +1,639 @@ 28 | +/** @file 29 | + This driver enables Intel Graphics Device (IGD) assignment with vfio-pci 30 | + according to QEMU's "docs/igd-assign.txt" specification. 31 | + 32 | + Copyright (C) 2018, Red Hat, Inc. 33 | + 34 | + This program and the accompanying materials are licensed and made available 35 | + under the terms and conditions of the BSD License which accompanies this 36 | + distribution. The full text of the license may be found at 37 | + http://opensource.org/licenses/bsd-license.php 38 | + 39 | + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT 40 | + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 41 | +**/ 42 | + 43 | +#include 44 | +#include 45 | + 46 | +#include 47 | +#include 48 | +#include 49 | +#include 50 | +#include 51 | + 52 | +#include 53 | +#include 54 | + 55 | +//#define QM_DEBUG 56 | + 57 | +#ifdef QM_DEBUG 58 | +EFI_SERIAL_IO_PROTOCOL *Serial; 59 | +EFI_GUID gEfiSerialIoProtocolGuid_dedup=EFI_SERIAL_IO_PROTOCOL_GUID; 60 | + 61 | +static CHAR8 bigbuf[65536]; 62 | +static EFIAPI void RealDebugPrint(int shit,const CHAR8* format,...){ 63 | + VA_LIST Marker; 64 | + VA_START(Marker,format); 65 | + UINTN n=AsciiVSPrint(bigbuf,32768,format,Marker); 66 | + VA_END(Marker); 67 | + Serial->Write(Serial,&n,bigbuf); 68 | +} 69 | + 70 | +#undef DEBUG 71 | +#define DEBUG(A) RealDebugPrint A 72 | +#else 73 | +#undef DEBUG 74 | +#define DEBUG(A) 75 | +#endif 76 | + 77 | +// 78 | +// structure that collects information from PCI config space that is needed to 79 | +// evaluate whether IGD assignment applies to the device 80 | +// 81 | +typedef struct { 82 | + UINT16 VendorId; 83 | + UINT8 ClassCode[3]; 84 | + UINTN Segment; 85 | + UINTN Bus; 86 | + UINTN Device; 87 | + UINTN Function; 88 | + CHAR8 Name[sizeof "0000:00:02.0"]; 89 | +} CANDIDATE_PCI_INFO; 90 | + 91 | +// 92 | +// selector and size of ASSIGNED_IGD_FW_CFG_OPREGION 93 | +// 94 | +STATIC FIRMWARE_CONFIG_ITEM mOpRegionItem; 95 | +STATIC UINTN mOpRegionSize; 96 | +// 97 | +// value read from ASSIGNED_IGD_FW_CFG_BDSM_SIZE, converted to UINTN 98 | +// 99 | +STATIC UINTN mBdsmSize; 100 | +// 101 | +// gBS->LocateProtocol() helper for finding the next unhandled PciIo instance 102 | +// 103 | +STATIC VOID *mPciIoTracker; 104 | + 105 | + 106 | +/** 107 | + Populate the CANDIDATE_PCI_INFO structure for a PciIo protocol instance. 108 | + 109 | + @param[in] PciIo EFI_PCI_IO_PROTOCOL instance to interrogate. 110 | + 111 | + @param[out] PciInfo CANDIDATE_PCI_INFO structure to fill. 112 | + 113 | + @retval EFI_SUCCESS PciInfo has been filled in. PciInfo->Name has been set 114 | + to the empty string. 115 | + 116 | + @return Error codes from PciIo->Pci.Read() and 117 | + PciIo->GetLocation(). The contents of PciInfo are 118 | + indeterminate. 119 | +**/ 120 | +STATIC 121 | +EFI_STATUS 122 | +InitPciInfo ( 123 | + IN EFI_PCI_IO_PROTOCOL *PciIo, 124 | + OUT CANDIDATE_PCI_INFO *PciInfo 125 | + ) 126 | +{ 127 | + EFI_STATUS Status; 128 | + 129 | + Status = PciIo->Pci.Read ( 130 | + PciIo, 131 | + EfiPciIoWidthUint16, 132 | + PCI_VENDOR_ID_OFFSET, 133 | + 1, // Count 134 | + &PciInfo->VendorId 135 | + ); 136 | + if (EFI_ERROR (Status)) { 137 | + return Status; 138 | + } 139 | + 140 | + Status = PciIo->Pci.Read ( 141 | + PciIo, 142 | + EfiPciIoWidthUint8, 143 | + PCI_CLASSCODE_OFFSET, 144 | + sizeof PciInfo->ClassCode, 145 | + PciInfo->ClassCode 146 | + ); 147 | + if (EFI_ERROR (Status)) { 148 | + return Status; 149 | + } 150 | + 151 | + Status = PciIo->GetLocation ( 152 | + PciIo, 153 | + &PciInfo->Segment, 154 | + &PciInfo->Bus, 155 | + &PciInfo->Device, 156 | + &PciInfo->Function 157 | + ); 158 | + if (EFI_ERROR (Status)) { 159 | + return Status; 160 | + } 161 | + 162 | + PciInfo->Name[0] = '\0'; 163 | + return EFI_SUCCESS; 164 | +} 165 | + 166 | +#ifdef QM_DEBUG 167 | +/** 168 | + Format and get the debug name of a CANDIDATE_PCI_INFO structure. 169 | + 170 | + param[in,out] PciInfo If the PciInfo->Name member is an empty string, format 171 | + the PCI bus address of PciInfo into PciInfo->Name. 172 | + Otherwise, don't modify PciInfo. 173 | + 174 | + @return PciInfo->Name 175 | +**/ 176 | +STATIC 177 | +CONST CHAR8 * 178 | +GetPciName ( 179 | + IN OUT CANDIDATE_PCI_INFO *PciInfo 180 | + ) 181 | +{ 182 | + if (PciInfo->Name[0] == '\0') { 183 | + AsciiSPrint ( 184 | + PciInfo->Name, 185 | + sizeof PciInfo->Name, 186 | + "%04x:%02x:%02x.%x", 187 | + (UINT16)PciInfo->Segment, 188 | + (UINT8)PciInfo->Bus, 189 | + (UINT8)PciInfo->Device, 190 | + (UINT32)PciInfo->Function & 0xf 191 | + ); 192 | + } 193 | + return PciInfo->Name; 194 | +} 195 | +#endif 196 | + 197 | +/** 198 | + Allocate memory in the 32-bit address space, with the requested UEFI memory 199 | + type and the requested alignment. 200 | + 201 | + @param[in] MemoryType Assign MemoryType to the allocated pages as 202 | + memory type. 203 | + 204 | + @param[in] NumberOfPages The number of pages to allocate. 205 | + 206 | + @param[in] AlignmentInPages On output, Address will be a whole multiple of 207 | + EFI_PAGES_TO_SIZE (AlignmentInPages). 208 | + AlignmentInPages must be a power of two. 209 | + 210 | + @param[out] Address Base address of the allocated area. 211 | + 212 | + @retval EFI_SUCCESS Allocation successful. 213 | + 214 | + @retval EFI_INVALID_PARAMETER AlignmentInPages is not a power of two (a 215 | + special case of which is when AlignmentInPages 216 | + is zero). 217 | + 218 | + @retval EFI_OUT_OF_RESOURCES Integer overflow detected. 219 | + 220 | + @return Error codes from gBS->AllocatePages(). 221 | +**/ 222 | +STATIC 223 | +EFI_STATUS 224 | +Allocate32BitAlignedPagesWithType ( 225 | + IN EFI_MEMORY_TYPE MemoryType, 226 | + IN UINTN NumberOfPages, 227 | + IN UINTN AlignmentInPages, 228 | + OUT EFI_PHYSICAL_ADDRESS *Address 229 | + ) 230 | +{ 231 | + EFI_STATUS Status; 232 | + EFI_PHYSICAL_ADDRESS PageAlignedAddress; 233 | + EFI_PHYSICAL_ADDRESS FullyAlignedAddress; 234 | + UINTN BottomPages; 235 | + UINTN TopPages; 236 | + 237 | + // 238 | + // AlignmentInPages must be a power of two. 239 | + // 240 | + if (AlignmentInPages == 0 || 241 | + (AlignmentInPages & (AlignmentInPages - 1)) != 0) { 242 | + return EFI_INVALID_PARAMETER; 243 | + } 244 | + // 245 | + // (NumberOfPages + (AlignmentInPages - 1)) must not overflow UINTN. 246 | + // 247 | + if (AlignmentInPages - 1 > MAX_UINTN - NumberOfPages) { 248 | + return EFI_OUT_OF_RESOURCES; 249 | + } 250 | + // 251 | + // EFI_PAGES_TO_SIZE (AlignmentInPages) must not overflow UINTN. 252 | + // 253 | + if (AlignmentInPages > (MAX_UINTN >> EFI_PAGE_SHIFT)) { 254 | + return EFI_OUT_OF_RESOURCES; 255 | + } 256 | + 257 | + // 258 | + // Allocate with sufficient padding for alignment. 259 | + // 260 | + PageAlignedAddress = BASE_4GB - 1; 261 | + //PageAlignedAddress = BASE_2GB - 1; 262 | + Status = gBS->AllocatePages ( 263 | + AllocateMaxAddress, 264 | + MemoryType, 265 | + NumberOfPages + (AlignmentInPages - 1), 266 | + &PageAlignedAddress 267 | + ); 268 | + if (EFI_ERROR (Status)) { 269 | + return Status; 270 | + } 271 | + FullyAlignedAddress = ALIGN_VALUE ( 272 | + PageAlignedAddress, 273 | + (UINT64)EFI_PAGES_TO_SIZE (AlignmentInPages) 274 | + ); 275 | + 276 | + // 277 | + // Release bottom and/or top padding. 278 | + // 279 | + BottomPages = EFI_SIZE_TO_PAGES ( 280 | + (UINTN)(FullyAlignedAddress - PageAlignedAddress) 281 | + ); 282 | + TopPages = (AlignmentInPages - 1) - BottomPages; 283 | + if (BottomPages > 0) { 284 | + Status = gBS->FreePages (PageAlignedAddress, BottomPages); 285 | + ASSERT_EFI_ERROR (Status); 286 | + } 287 | + if (TopPages > 0) { 288 | + Status = gBS->FreePages ( 289 | + FullyAlignedAddress + EFI_PAGES_TO_SIZE (NumberOfPages), 290 | + TopPages 291 | + ); 292 | + ASSERT_EFI_ERROR (Status); 293 | + } 294 | + 295 | + *Address = FullyAlignedAddress; 296 | + return EFI_SUCCESS; 297 | +} 298 | + 299 | +//CHAR8 OPREGION_SIGNATURE[]="IntelGraphicsMem"; 300 | + 301 | +/** 302 | + Set up the OpRegion for the device identified by PciIo. 303 | + 304 | + @param[in] PciIo The device to set up the OpRegion for. 305 | + 306 | + @param[in,out] PciInfo On input, PciInfo must have been initialized from 307 | + PciIo with InitPciInfo(). SetupOpRegion() may call 308 | + GetPciName() on PciInfo, possibly modifying it. 309 | + 310 | + @retval EFI_SUCCESS OpRegion setup successful. 311 | + 312 | + @retval EFI_INVALID_PARAMETER mOpRegionSize is zero. 313 | + 314 | + @return Error codes propagated from underlying 315 | + functions. 316 | +**/ 317 | +STATIC 318 | +EFI_STATUS 319 | +SetupOpRegion ( 320 | + IN EFI_PCI_IO_PROTOCOL *PciIo, 321 | + IN OUT CANDIDATE_PCI_INFO *PciInfo 322 | + ) 323 | +{ 324 | + UINTN OpRegionPages; 325 | + UINTN OpRegionResidual; 326 | + EFI_STATUS Status; 327 | + EFI_PHYSICAL_ADDRESS Address; 328 | + UINT8 *BytePointer; 329 | + 330 | + if (mOpRegionSize == 0) { 331 | + return EFI_INVALID_PARAMETER; 332 | + } 333 | + OpRegionPages = EFI_SIZE_TO_PAGES (mOpRegionSize); 334 | + OpRegionResidual = EFI_PAGES_TO_SIZE (OpRegionPages) - mOpRegionSize; 335 | + 336 | + // 337 | + // While QEMU's "docs/igd-assign.txt" specifies reserved memory, Intel's IGD 338 | + // OpRegion spec refers to ACPI NVS. 339 | + // 340 | + Status = Allocate32BitAlignedPagesWithType ( 341 | + EfiACPIMemoryNVS, 342 | + OpRegionPages, 343 | + 1, // AlignmentInPages 344 | + &Address 345 | + ); 346 | + if (EFI_ERROR (Status)) { 347 | + DEBUG ((DEBUG_ERROR, "%a: %a: failed to allocate OpRegion: %r\n", 348 | + __FUNCTION__, GetPciName (PciInfo), Status)); 349 | + return Status; 350 | + } 351 | + 352 | + // 353 | + // Download OpRegion contents from fw_cfg, zero out trailing portion. 354 | + // 355 | + BytePointer = (UINT8 *)(UINTN)Address; 356 | + QemuFwCfgSelectItem (mOpRegionItem); 357 | + QemuFwCfgReadBytes (mOpRegionSize, BytePointer); 358 | + if(OpRegionResidual){ 359 | + ZeroMem (BytePointer + mOpRegionSize, OpRegionResidual); 360 | + } 361 | + 362 | + //for(int i=0;iWrite(Serial,&n,(CHAR8*)BytePointer); 371 | + } 372 | + #endif 373 | + 374 | + // 375 | + // Write address of OpRegion to PCI config space. 376 | + // 377 | + Status = PciIo->Pci.Write ( 378 | + PciIo, 379 | + EfiPciIoWidthUint32, 380 | + ASSIGNED_IGD_PCI_ASLS_OFFSET, 381 | + 1, // Count 382 | + &Address 383 | + ); 384 | + if (EFI_ERROR (Status)) { 385 | + DEBUG ((DEBUG_ERROR, "%a: %a: failed to write OpRegion address: %r\n", 386 | + __FUNCTION__, GetPciName (PciInfo), Status)); 387 | + goto FreeOpRegion; 388 | + } 389 | + 390 | + DEBUG ((DEBUG_INFO, "%a: %a: OpRegion @ 0x%Lx size 0x%Lx in %d pages\n", __FUNCTION__, 391 | + GetPciName (PciInfo), Address, (UINT64)mOpRegionSize,(int)OpRegionPages)); 392 | + return EFI_SUCCESS; 393 | + 394 | +FreeOpRegion: 395 | + gBS->FreePages (Address, OpRegionPages); 396 | + return Status; 397 | +} 398 | + 399 | + 400 | +/** 401 | + Set up stolen memory for the device identified by PciIo. 402 | + 403 | + @param[in] PciIo The device to set up stolen memory for. 404 | + 405 | + @param[in,out] PciInfo On input, PciInfo must have been initialized from 406 | + PciIo with InitPciInfo(). SetupStolenMemory() may 407 | + call GetPciName() on PciInfo, possibly modifying it. 408 | + 409 | + @retval EFI_SUCCESS Stolen memory setup successful. 410 | + 411 | + @retval EFI_INVALID_PARAMETER mBdsmSize is zero. 412 | + 413 | + @return Error codes propagated from underlying 414 | + functions. 415 | +**/ 416 | +STATIC 417 | +EFI_STATUS 418 | +SetupStolenMemory ( 419 | + IN EFI_PCI_IO_PROTOCOL *PciIo, 420 | + IN OUT CANDIDATE_PCI_INFO *PciInfo 421 | + ) 422 | +{ 423 | + UINTN BdsmPages; 424 | + EFI_STATUS Status; 425 | + EFI_PHYSICAL_ADDRESS Address; 426 | + 427 | + if (mBdsmSize == 0) { 428 | + return EFI_INVALID_PARAMETER; 429 | + } 430 | + BdsmPages = EFI_SIZE_TO_PAGES (mBdsmSize); 431 | + 432 | + Status = Allocate32BitAlignedPagesWithType ( 433 | + EfiReservedMemoryType,// 434 | + BdsmPages, 435 | + EFI_SIZE_TO_PAGES ((UINTN)ASSIGNED_IGD_BDSM_ALIGN), 436 | + &Address 437 | + ); 438 | + if (EFI_ERROR (Status)) { 439 | + DEBUG ((DEBUG_ERROR, "%a: %a: failed to allocate stolen memory: %r\n", 440 | + __FUNCTION__, GetPciName (PciInfo), Status)); 441 | + return Status; 442 | + } 443 | + 444 | + // 445 | + // Zero out stolen memory. 446 | + // 447 | + ZeroMem ((VOID *)(UINTN)Address, EFI_PAGES_TO_SIZE (BdsmPages)); 448 | + 449 | + // 450 | + // Write address of stolen memory to PCI config space. 451 | + // 452 | + Status = PciIo->Pci.Write ( 453 | + PciIo, 454 | + EfiPciIoWidthUint32, 455 | + ASSIGNED_IGD_PCI_BDSM_OFFSET, 456 | + 1, // Count 457 | + &Address 458 | + ); 459 | + if (EFI_ERROR (Status)) { 460 | + DEBUG ((DEBUG_ERROR, "%a: %a: failed to write stolen memory address: %r\n", 461 | + __FUNCTION__, GetPciName (PciInfo), Status)); 462 | + goto FreeStolenMemory; 463 | + } 464 | + 465 | + DEBUG ((DEBUG_INFO, "%a: %a: stolen memory @ 0x%Lx size 0x%Lx\n", 466 | + __FUNCTION__, GetPciName (PciInfo), Address, (UINT64)mBdsmSize)); 467 | + return EFI_SUCCESS; 468 | + 469 | +FreeStolenMemory: 470 | + gBS->FreePages (Address, BdsmPages); 471 | + return Status; 472 | +} 473 | + 474 | + 475 | +/** 476 | + Process any PciIo protocol instances that may have been installed since the 477 | + last invocation. 478 | + 479 | + @param[in] Event Event whose notification function is being invoked. 480 | + 481 | + @param[in] Context The pointer to the notification function's context. 482 | +**/ 483 | +STATIC 484 | +VOID 485 | +EFIAPI 486 | +PciIoNotify ( 487 | + IN EFI_EVENT Event, 488 | + IN VOID *Context 489 | + ) 490 | +{ 491 | + EFI_PCI_IO_PROTOCOL *PciIo; 492 | + 493 | + while (!EFI_ERROR (gBS->LocateProtocol ( 494 | + &gEfiPciIoProtocolGuid, 495 | + mPciIoTracker, 496 | + (VOID **)&PciIo 497 | + ))) { 498 | + EFI_STATUS Status; 499 | + CANDIDATE_PCI_INFO PciInfo; 500 | + 501 | + Status = InitPciInfo (PciIo, &PciInfo); 502 | + if (EFI_ERROR (Status)) { 503 | + DEBUG ((DEBUG_ERROR, "%a: InitPciInfo (PciIo@%p): %r\n", __FUNCTION__, 504 | + (VOID *)PciIo, Status)); 505 | + continue; 506 | + } 507 | + 508 | + // 509 | + // Check VendorId and ClassCode. These checks are necessary for both 510 | + // OpRegion and stolen memory setup. 511 | + // 512 | + if (PciInfo.VendorId != ASSIGNED_IGD_PCI_VENDOR_ID || 513 | + PciInfo.ClassCode[2] != PCI_CLASS_DISPLAY || 514 | + PciInfo.ClassCode[1] != PCI_CLASS_DISPLAY_VGA || 515 | + PciInfo.ClassCode[0] != PCI_IF_VGA_VGA) { 516 | + continue; 517 | + } 518 | + 519 | + if (mOpRegionSize > 0) { 520 | + SetupOpRegion (PciIo, &PciInfo); 521 | + } 522 | + 523 | + // 524 | + // Check Bus:Device.Function (Segment is ignored). This is necessary before 525 | + // stolen memory setup. 526 | + // 527 | + if (PciInfo.Bus != ASSIGNED_IGD_PCI_BUS || 528 | + PciInfo.Device != ASSIGNED_IGD_PCI_DEVICE || 529 | + PciInfo.Function != ASSIGNED_IGD_PCI_FUNCTION) { 530 | + continue; 531 | + } 532 | + 533 | + if (mBdsmSize > 0) { 534 | + SetupStolenMemory (PciIo, &PciInfo); 535 | + } 536 | + } 537 | +} 538 | + 539 | +/** 540 | + Entry point for this driver. 541 | + 542 | + @param[in] ImageHandle Image handle of this driver. 543 | + 544 | + @param[in] SystemTable Pointer to SystemTable. 545 | + 546 | + @retval EFI_SUCESS Driver has loaded successfully. 547 | + 548 | + @retval EFI_UNSUPPORTED No IGD assigned. 549 | + 550 | + @retval EFI_PROTOCOL_ERROR Invalid fw_cfg contents. 551 | + 552 | + @return Error codes propagated from underlying functions. 553 | +**/ 554 | +EFI_STATUS 555 | +EFIAPI 556 | +IgdAssignmentEntry ( 557 | + IN EFI_HANDLE ImageHandle, 558 | + IN EFI_SYSTEM_TABLE *SystemTable 559 | + ) 560 | +{ 561 | + EFI_STATUS OpRegionStatus; 562 | + EFI_STATUS BdsmStatus; 563 | + FIRMWARE_CONFIG_ITEM BdsmItem; 564 | + UINTN BdsmItemSize; 565 | + EFI_STATUS Status; 566 | + EFI_EVENT PciIoEvent; 567 | + 568 | + #ifdef QM_DEBUG 569 | + gBS->LocateProtocol( 570 | + &gEfiSerialIoProtocolGuid_dedup, 571 | + NULL, 572 | + (VOID **)&Serial); 573 | + #endif 574 | + 575 | + OpRegionStatus = QemuFwCfgFindFile ( 576 | + ASSIGNED_IGD_FW_CFG_OPREGION, 577 | + &mOpRegionItem, 578 | + &mOpRegionSize 579 | + ); 580 | + BdsmStatus = QemuFwCfgFindFile ( 581 | + ASSIGNED_IGD_FW_CFG_BDSM_SIZE, 582 | + &BdsmItem, 583 | + &BdsmItemSize 584 | + ); 585 | + // 586 | + // If neither fw_cfg file is available, assume no IGD is assigned. 587 | + // 588 | + if (EFI_ERROR (OpRegionStatus) && EFI_ERROR (BdsmStatus)) { 589 | + return EFI_UNSUPPORTED; 590 | + } 591 | + 592 | + // 593 | + // Require all fw_cfg files that are present to be well-formed. 594 | + // 595 | + if (!EFI_ERROR (OpRegionStatus) && mOpRegionSize == 0) { 596 | + DEBUG ((DEBUG_ERROR, "%a: %a: zero size\n", __FUNCTION__, 597 | + ASSIGNED_IGD_FW_CFG_OPREGION)); 598 | + return EFI_PROTOCOL_ERROR; 599 | + } 600 | + 601 | + if (!EFI_ERROR (BdsmStatus)) { 602 | + UINT64 BdsmSize; 603 | + 604 | + if (BdsmItemSize != sizeof BdsmSize) { 605 | + DEBUG ((DEBUG_ERROR, "%a: %a: invalid fw_cfg size: %Lu\n", __FUNCTION__, 606 | + ASSIGNED_IGD_FW_CFG_BDSM_SIZE, (UINT64)BdsmItemSize)); 607 | + return EFI_PROTOCOL_ERROR; 608 | + } 609 | + QemuFwCfgSelectItem (BdsmItem); 610 | + QemuFwCfgReadBytes (BdsmItemSize, &BdsmSize); 611 | + 612 | + if (BdsmSize == 0 || BdsmSize > MAX_UINTN) { 613 | + DEBUG ((DEBUG_ERROR, "%a: %a: invalid value: %Lu\n", __FUNCTION__, 614 | + ASSIGNED_IGD_FW_CFG_BDSM_SIZE, BdsmSize)); 615 | + return EFI_PROTOCOL_ERROR; 616 | + } 617 | + DEBUG((DEBUG_INFO,"BdsmSize=%Lu\n",BdsmSize)); 618 | + mBdsmSize = (UINTN)BdsmSize; 619 | + }else{ 620 | + //assume 64M 621 | + DEBUG((DEBUG_INFO,"BdsmSize not found\n")); 622 | + //mBdsmSize = (UINTN)(64<<20); 623 | + } 624 | + 625 | + // 626 | + // At least one valid fw_cfg file has been found. 627 | + // 628 | + ASSERT (mOpRegionSize > 0 || mBdsmSize > 0); 629 | + 630 | + // 631 | + // Register PciIo protocol installation callback. 632 | + // 633 | + Status = gBS->CreateEvent ( 634 | + EVT_NOTIFY_SIGNAL, 635 | + TPL_CALLBACK, 636 | + PciIoNotify, 637 | + NULL, // Context 638 | + &PciIoEvent 639 | + ); 640 | + if (EFI_ERROR (Status)) { 641 | + return Status; 642 | + } 643 | + Status = gBS->RegisterProtocolNotify ( 644 | + &gEfiPciIoProtocolGuid, 645 | + PciIoEvent, 646 | + &mPciIoTracker 647 | + ); 648 | + if (EFI_ERROR (Status)) { 649 | + goto ClosePciIoEvent; 650 | + } 651 | + 652 | + // 653 | + // Kick the event for any existent PciIo protocol instances. 654 | + // 655 | + Status = gBS->SignalEvent (PciIoEvent); 656 | + if (EFI_ERROR (Status)) { 657 | + goto ClosePciIoEvent; 658 | + } 659 | + 660 | + return EFI_SUCCESS; 661 | + 662 | +ClosePciIoEvent: 663 | + gBS->CloseEvent (PciIoEvent); 664 | + 665 | + return Status; 666 | +} 667 | diff --git a/OvmfPkg/IgdAssignmentDxe/IgdAssignment.inf b/OvmfPkg/IgdAssignmentDxe/IgdAssignment.inf 668 | new file mode 100644 669 | index 00000000000..7b856e98091 670 | --- /dev/null 671 | +++ b/OvmfPkg/IgdAssignmentDxe/IgdAssignment.inf 672 | @@ -0,0 +1,44 @@ 673 | +## @file 674 | +# This driver enables Intel Graphics Device (IGD) assignment with vfio-pci 675 | +# according to QEMU's "docs/igd-assign.txt" specification. 676 | +# 677 | +# Copyright (C) 2018, Red Hat, Inc. 678 | +# 679 | +# This program and the accompanying materials are licensed and made available 680 | +# under the terms and conditions of the BSD License which accompanies this 681 | +# distribution. The full text of the license may be found at 682 | +# http://opensource.org/licenses/bsd-license.php 683 | +# 684 | +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT 685 | +# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 686 | +## 687 | + 688 | +[Defines] 689 | + INF_VERSION = 1.27 690 | + BASE_NAME = IgdAssignmentDxe 691 | + FILE_GUID = FDD95992-8F3B-4ADD-8DEC-989EA84E9DF1 692 | + MODULE_TYPE = DXE_DRIVER 693 | + VERSION_STRING = 1.0 694 | + ENTRY_POINT = IgdAssignmentEntry 695 | + 696 | +[Sources] 697 | + IgdAssignment.c 698 | + 699 | +[Packages] 700 | + MdePkg/MdePkg.dec 701 | + OvmfPkg/OvmfPkg.dec 702 | + 703 | +[LibraryClasses] 704 | + BaseMemoryLib 705 | + DebugLib 706 | + PrintLib 707 | + QemuFwCfgLib 708 | + UefiBootServicesTableLib 709 | + UefiDriverEntryPoint 710 | + 711 | +[Protocols] 712 | + gEfiPciIoProtocolGuid ## SOMETIMES_CONSUMES ## NOTIFY 713 | + gEfiSerialIoProtocolGuid ## CONSUMES 714 | + 715 | +[Depex] 716 | + gEfiSerialIoProtocolGuid 717 | diff --git a/OvmfPkg/Include/IndustryStandard/AssignedIgd.h b/OvmfPkg/Include/IndustryStandard/AssignedIgd.h 718 | new file mode 100644 719 | index 00000000000..39251b89de6 720 | --- /dev/null 721 | +++ b/OvmfPkg/Include/IndustryStandard/AssignedIgd.h 722 | @@ -0,0 +1,50 @@ 723 | +/** @file 724 | + Macros corresponding to QEMU's "Intel Graphics Device (IGD) assignment with 725 | + vfio-pci" specification, located at "docs/igd-assign.txt" in the QEMU source 726 | + tree. 727 | + 728 | + Copyright (C) 2018, Red Hat, Inc. 729 | + 730 | + This program and the accompanying materials are licensed and made available 731 | + under the terms and conditions of the BSD License which accompanies this 732 | + distribution. The full text of the license may be found at 733 | + http://opensource.org/licenses/bsd-license.php 734 | + 735 | + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT 736 | + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 737 | +**/ 738 | + 739 | +#ifndef _ASSIGNED_IGD_H_ 740 | +#define _ASSIGNED_IGD_H_ 741 | + 742 | +#include 743 | + 744 | +// 745 | +// names of fw_cfg files 746 | +// 747 | +#define ASSIGNED_IGD_FW_CFG_OPREGION "etc/igd-opregion" 748 | +#define ASSIGNED_IGD_FW_CFG_BDSM_SIZE "etc/igd-bdsm-size" 749 | + 750 | +// 751 | +// Alignment constants. UEFI page allocation automatically satisfies the 752 | +// requirements for the OpRegion, thus we only need to define an alignment 753 | +// constant for IGD stolen memory. 754 | +// 755 | +#define ASSIGNED_IGD_BDSM_ALIGN SIZE_1MB 756 | + 757 | +// 758 | +// PCI config space registers. The naming follows the PCI_*_OFFSET pattern seen 759 | +// in MdePkg/Include/IndustryStandard/Pci*.h. 760 | +// 761 | +#define ASSIGNED_IGD_PCI_BDSM_OFFSET 0x5C 762 | +#define ASSIGNED_IGD_PCI_ASLS_OFFSET 0xFC 763 | + 764 | +// 765 | +// PCI location and vendor 766 | +// 767 | +#define ASSIGNED_IGD_PCI_BUS 0x00 768 | +#define ASSIGNED_IGD_PCI_DEVICE 0x02 769 | +#define ASSIGNED_IGD_PCI_FUNCTION 0x0 770 | +#define ASSIGNED_IGD_PCI_VENDOR_ID 0x8086 771 | + 772 | +#endif // _ASSIGNED_IGD_H_ 773 | diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc 774 | index f9216af479f..354c0c7b7ff 100644 775 | --- a/OvmfPkg/OvmfPkgIa32.dsc 776 | +++ b/OvmfPkg/OvmfPkgIa32.dsc 777 | @@ -747,6 +747,7 @@ 778 | OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf 779 | OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf 780 | OvmfPkg/VirtioGpuDxe/VirtioGpu.inf 781 | + OvmfPkg/IgdAssignmentDxe/IgdAssignment.inf 782 | 783 | # 784 | # ISA Support 785 | diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf 786 | index 4999403ad7f..a4c6f5eb03f 100644 787 | --- a/OvmfPkg/OvmfPkgIa32.fdf 788 | +++ b/OvmfPkg/OvmfPkgIa32.fdf 789 | @@ -347,6 +347,7 @@ INF RuleOverride=CSM OvmfPkg/Csm/Csm16/Csm16.inf 790 | INF OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf 791 | INF OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf 792 | INF OvmfPkg/VirtioGpuDxe/VirtioGpu.inf 793 | +INF OvmfPkg/IgdAssignmentDxe/IgdAssignment.inf 794 | INF OvmfPkg/PlatformDxe/Platform.inf 795 | INF OvmfPkg/IoMmuDxe/IoMmuDxe.inf 796 | 797 | diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc 798 | index 1e470de7443..63c122aa087 100644 799 | --- a/OvmfPkg/OvmfPkgIa32X64.dsc 800 | +++ b/OvmfPkg/OvmfPkgIa32X64.dsc 801 | @@ -756,6 +756,7 @@ 802 | OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf 803 | OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf 804 | OvmfPkg/VirtioGpuDxe/VirtioGpu.inf 805 | + OvmfPkg/IgdAssignmentDxe/IgdAssignment.inf 806 | 807 | # 808 | # ISA Support 809 | diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf 810 | index d0cc1079287..0895391b93d 100644 811 | --- a/OvmfPkg/OvmfPkgIa32X64.fdf 812 | +++ b/OvmfPkg/OvmfPkgIa32X64.fdf 813 | @@ -353,6 +353,7 @@ INF RuleOverride=CSM OvmfPkg/Csm/Csm16/Csm16.inf 814 | INF OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf 815 | INF OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf 816 | INF OvmfPkg/VirtioGpuDxe/VirtioGpu.inf 817 | +INF OvmfPkg/IgdAssignmentDxe/IgdAssignment.inf 818 | INF OvmfPkg/PlatformDxe/Platform.inf 819 | INF OvmfPkg/AmdSevDxe/AmdSevDxe.inf 820 | INF OvmfPkg/IoMmuDxe/IoMmuDxe.inf 821 | diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc 822 | index e4929d8cf4a..28b42b10991 100644 823 | --- a/OvmfPkg/OvmfPkgX64.dsc 824 | +++ b/OvmfPkg/OvmfPkgX64.dsc 825 | @@ -754,6 +754,7 @@ 826 | OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf 827 | OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf 828 | OvmfPkg/VirtioGpuDxe/VirtioGpu.inf 829 | + OvmfPkg/IgdAssignmentDxe/IgdAssignment.inf 830 | 831 | # 832 | # ISA Support 833 | diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf 834 | index d0cc1079287..0895391b93d 100644 835 | --- a/OvmfPkg/OvmfPkgX64.fdf 836 | +++ b/OvmfPkg/OvmfPkgX64.fdf 837 | @@ -353,6 +353,7 @@ INF RuleOverride=CSM OvmfPkg/Csm/Csm16/Csm16.inf 838 | INF OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf 839 | INF OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf 840 | INF OvmfPkg/VirtioGpuDxe/VirtioGpu.inf 841 | +INF OvmfPkg/IgdAssignmentDxe/IgdAssignment.inf 842 | INF OvmfPkg/PlatformDxe/Platform.inf 843 | INF OvmfPkg/AmdSevDxe/AmdSevDxe.inf 844 | INF OvmfPkg/IoMmuDxe/IoMmuDxe.inf 845 | diff --git a/OvmfPkg/QemuRamfbDxe/QemuRamfb.c b/OvmfPkg/QemuRamfbDxe/QemuRamfb.c 846 | index b49f2ca6e89..587613ed635 100644 847 | --- a/OvmfPkg/QemuRamfbDxe/QemuRamfb.c 848 | +++ b/OvmfPkg/QemuRamfbDxe/QemuRamfb.c 849 | @@ -55,8 +55,8 @@ STATIC EFI_GRAPHICS_OUTPUT_MODE_INFORMATION mQemuRamfbModeInfo[] = { 850 | 480, // VerticalResolution 851 | },{ 852 | 0, // Version 853 | - 800, // HorizontalResolution 854 | - 600, // VerticalResolution 855 | + 1280, // HorizontalResolution 856 | + 720, // VerticalResolution 857 | },{ 858 | 0, // Version 859 | 1024, // HorizontalResolution 860 | @@ -255,9 +255,19 @@ InitializeQemuRamfb ( 861 | DEBUG ((DEBUG_ERROR, "Ramfb: FwCfg size mismatch (expected %lu, got %lu)\n", 862 | (UINT64)sizeof (RAMFB_CONFIG), (UINT64)FwCfgSize)); 863 | return EFI_PROTOCOL_ERROR; 864 | + } 865 | + 866 | + RAMFB_CONFIG TheirConfig; 867 | + ZeroMem(&TheirConfig,sizeof(TheirConfig)); 868 | + QemuFwCfgSelectItem (mRamfbFwCfgItem); 869 | + QemuFwCfgReadBytes(sizeof(TheirConfig),&TheirConfig); 870 | + if(TheirConfig.Width&&TheirConfig.Height){ 871 | + //update native res 872 | + mQemuRamfbModeInfo[1].HorizontalResolution=TheirConfig.Width; 873 | + mQemuRamfbModeInfo[1].VerticalResolution=TheirConfig.Height; 874 | } 875 | 876 | - MaxFbSize = 0; 877 | + MaxFbSize = 0;//4096*4096*4; 878 | for (Index = 0; Index < ARRAY_SIZE (mQemuRamfbModeInfo); Index++) { 879 | mQemuRamfbModeInfo[Index].PixelsPerScanLine = 880 | mQemuRamfbModeInfo[Index].HorizontalResolution; 881 | @@ -288,7 +298,7 @@ InitializeQemuRamfb ( 882 | mQemuRamfbMode.FrameBufferBase = FbBase; 883 | 884 | // 885 | - // 800 x 600 886 | + // 1280 x 720 887 | // 888 | QemuRamfbGraphicsOutputSetMode (&mQemuRamfbGraphicsOutput, 1); 889 | 890 | --------------------------------------------------------------------------------