diff options
Diffstat (limited to 'bolt/lib/Rewrite/LinuxKernelRewriter.cpp')
-rw-r--r-- | bolt/lib/Rewrite/LinuxKernelRewriter.cpp | 126 |
1 files changed, 93 insertions, 33 deletions
diff --git a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp index 331a61e7c3c2..a2bfd45a64e3 100644 --- a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp +++ b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp @@ -55,6 +55,11 @@ static cl::opt<bool> DumpParavirtualPatchSites( "dump-para-sites", cl::desc("dump Linux kernel paravitual patch sites"), cl::init(false), cl::Hidden, cl::cat(BoltCategory)); +static cl::opt<bool> + DumpPCIFixups("dump-pci-fixups", + cl::desc("dump Linux kernel PCI fixup table"), + cl::init(false), cl::Hidden, cl::cat(BoltCategory)); + static cl::opt<bool> DumpStaticCalls("dump-static-calls", cl::desc("dump Linux kernel static calls"), cl::init(false), cl::Hidden, @@ -181,6 +186,10 @@ class LinuxKernelRewriter final : public MetadataRewriter { /// Size of bug_entry struct. static constexpr size_t BUG_TABLE_ENTRY_SIZE = 12; + /// .pci_fixup section. + ErrorOr<BinarySection &> PCIFixupSection = std::errc::bad_address; + static constexpr size_t PCI_FIXUP_ENTRY_SIZE = 16; + /// Insert an LKMarker for a given code pointer \p PC from a non-code section /// \p SectionName. void insertLKMarker(uint64_t PC, uint64_t SectionOffset, @@ -190,9 +199,6 @@ class LinuxKernelRewriter final : public MetadataRewriter { /// Process linux kernel special sections and their relocations. void processLKSections(); - /// Process special linux kernel section, .pci_fixup. - void processLKPCIFixup(); - /// Process __ksymtab and __ksymtab_gpl. void processLKKSymtab(bool IsGPL = false); @@ -226,6 +232,9 @@ class LinuxKernelRewriter final : public MetadataRewriter { /// Read alternative instruction info from .altinstructions. Error readAltInstructions(); + /// Read .pci_fixup + Error readPCIFixupTable(); + /// Mark instructions referenced by kernel metadata. Error markInstructions(); @@ -256,6 +265,9 @@ public: if (Error E = readAltInstructions()) return E; + if (Error E = readPCIFixupTable()) + return E; + return Error::success(); } @@ -318,41 +330,11 @@ void LinuxKernelRewriter::insertLKMarker(uint64_t PC, uint64_t SectionOffset, } void LinuxKernelRewriter::processLKSections() { - processLKPCIFixup(); processLKKSymtab(); processLKKSymtab(true); processLKSMPLocks(); } -/// Process .pci_fixup section of Linux Kernel. -/// This section contains a list of entries for different PCI devices and their -/// corresponding hook handler (code pointer where the fixup -/// code resides, usually on x86_64 it is an entry PC relative 32 bit offset). -/// Documentation is in include/linux/pci.h. -void LinuxKernelRewriter::processLKPCIFixup() { - ErrorOr<BinarySection &> SectionOrError = - BC.getUniqueSectionByName(".pci_fixup"); - if (!SectionOrError) - return; - - const uint64_t SectionSize = SectionOrError->getSize(); - const uint64_t SectionAddress = SectionOrError->getAddress(); - assert((SectionSize % 16) == 0 && ".pci_fixup size is not a multiple of 16"); - - for (uint64_t I = 12; I + 4 <= SectionSize; I += 16) { - const uint64_t PC = SectionAddress + I; - ErrorOr<uint64_t> Offset = BC.getSignedValueAtAddress(PC, 4); - assert(Offset && "cannot read value from .pci_fixup"); - const int32_t SignedOffset = *Offset; - const uint64_t HookupAddress = PC + SignedOffset; - BinaryFunction *HookupFunction = - BC.getBinaryFunctionAtAddress(HookupAddress); - assert(HookupFunction && "expected function for entry in .pci_fixup"); - BC.addRelocation(PC, HookupFunction->getSymbol(), Relocation::getPC32(), 0, - *Offset); - } -} - /// Process __ksymtab[_gpl] sections of Linux Kernel. /// This section lists all the vmlinux symbols that kernel modules can access. /// @@ -1283,6 +1265,84 @@ Error LinuxKernelRewriter::readAltInstructions() { return Error::success(); } +/// When the Linux kernel needs to handle an error associated with a given PCI +/// device, it uses a table stored in .pci_fixup section to locate a fixup code +/// specific to the vendor and the problematic device. The section contains a +/// list of the following structures defined in include/linux/pci.h: +/// +/// struct pci_fixup { +/// u16 vendor; /* Or PCI_ANY_ID */ +/// u16 device; /* Or PCI_ANY_ID */ +/// u32 class; /* Or PCI_ANY_ID */ +/// unsigned int class_shift; /* should be 0, 8, 16 */ +/// int hook_offset; +/// }; +/// +/// Normally, the hook will point to a function start and we don't have to +/// update the pointer if we are not relocating functions. Hence, while reading +/// the table we validate this assumption. If a function has a fixup code in the +/// middle of its body, we issue a warning and ignore it. +Error LinuxKernelRewriter::readPCIFixupTable() { + PCIFixupSection = BC.getUniqueSectionByName(".pci_fixup"); + if (!PCIFixupSection) + return Error::success(); + + if (PCIFixupSection->getSize() % PCI_FIXUP_ENTRY_SIZE) + return createStringError(errc::executable_format_error, + "PCI fixup table size error"); + + const uint64_t Address = PCIFixupSection->getAddress(); + DataExtractor DE = DataExtractor(PCIFixupSection->getContents(), + BC.AsmInfo->isLittleEndian(), + BC.AsmInfo->getCodePointerSize()); + uint64_t EntryID = 0; + DataExtractor::Cursor Cursor(0); + while (Cursor && !DE.eof(Cursor)) { + const uint16_t Vendor = DE.getU16(Cursor); + const uint16_t Device = DE.getU16(Cursor); + const uint32_t Class = DE.getU32(Cursor); + const uint32_t ClassShift = DE.getU32(Cursor); + const uint64_t HookAddress = + Address + Cursor.tell() + (int32_t)DE.getU32(Cursor); + + if (!Cursor) + return createStringError(errc::executable_format_error, + "out of bounds while reading .pci_fixup: %s", + toString(Cursor.takeError()).c_str()); + + ++EntryID; + + if (opts::DumpPCIFixups) { + BC.outs() << "PCI fixup entry: " << EntryID << "\n\tVendor 0x" + << Twine::utohexstr(Vendor) << "\n\tDevice: 0x" + << Twine::utohexstr(Device) << "\n\tClass: 0x" + << Twine::utohexstr(Class) << "\n\tClassShift: 0x" + << Twine::utohexstr(ClassShift) << "\n\tHookAddress: 0x" + << Twine::utohexstr(HookAddress) << '\n'; + } + + BinaryFunction *BF = BC.getBinaryFunctionContainingAddress(HookAddress); + if (!BF && opts::Verbosity) { + BC.outs() << "BOLT-INFO: no function matches address 0x" + << Twine::utohexstr(HookAddress) + << " of hook from .pci_fixup\n"; + } + + if (!BF || !BC.shouldEmit(*BF)) + continue; + + if (const uint64_t Offset = HookAddress - BF->getAddress()) { + BC.errs() << "BOLT-WARNING: PCI fixup detected in the middle of function " + << *BF << " at offset 0x" << Twine::utohexstr(Offset) << '\n'; + BF->setSimple(false); + } + } + + BC.outs() << "BOLT-INFO: parsed " << EntryID << " PCI fixup entries\n"; + + return Error::success(); +} + } // namespace std::unique_ptr<MetadataRewriter> |