@@ -55,6 +55,11 @@ static cl::opt<bool> DumpParavirtualPatchSites(
55
55
" dump-para-sites" , cl::desc(" dump Linux kernel paravitual patch sites" ),
56
56
cl::init(false ), cl::Hidden, cl::cat(BoltCategory));
57
57
58
+ static cl::opt<bool >
59
+ DumpPCIFixups (" dump-pci-fixups" ,
60
+ cl::desc (" dump Linux kernel PCI fixup table" ),
61
+ cl::init(false ), cl::Hidden, cl::cat(BoltCategory));
62
+
58
63
static cl::opt<bool > DumpStaticCalls (" dump-static-calls" ,
59
64
cl::desc (" dump Linux kernel static calls" ),
60
65
cl::init(false ), cl::Hidden,
@@ -181,6 +186,10 @@ class LinuxKernelRewriter final : public MetadataRewriter {
181
186
// / Size of bug_entry struct.
182
187
static constexpr size_t BUG_TABLE_ENTRY_SIZE = 12 ;
183
188
189
+ // / .pci_fixup section.
190
+ ErrorOr<BinarySection &> PCIFixupSection = std::errc::bad_address;
191
+ static constexpr size_t PCI_FIXUP_ENTRY_SIZE = 16 ;
192
+
184
193
// / Insert an LKMarker for a given code pointer \p PC from a non-code section
185
194
// / \p SectionName.
186
195
void insertLKMarker (uint64_t PC, uint64_t SectionOffset,
@@ -190,9 +199,6 @@ class LinuxKernelRewriter final : public MetadataRewriter {
190
199
// / Process linux kernel special sections and their relocations.
191
200
void processLKSections ();
192
201
193
- // / Process special linux kernel section, .pci_fixup.
194
- void processLKPCIFixup ();
195
-
196
202
// / Process __ksymtab and __ksymtab_gpl.
197
203
void processLKKSymtab (bool IsGPL = false );
198
204
@@ -226,6 +232,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
226
232
// / Read alternative instruction info from .altinstructions.
227
233
Error readAltInstructions ();
228
234
235
+ // / Read .pci_fixup
236
+ Error readPCIFixupTable ();
237
+
229
238
// / Mark instructions referenced by kernel metadata.
230
239
Error markInstructions ();
231
240
@@ -256,6 +265,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
256
265
if (Error E = readAltInstructions ())
257
266
return E;
258
267
268
+ if (Error E = readPCIFixupTable ())
269
+ return E;
270
+
259
271
return Error::success ();
260
272
}
261
273
@@ -318,41 +330,11 @@ void LinuxKernelRewriter::insertLKMarker(uint64_t PC, uint64_t SectionOffset,
318
330
}
319
331
320
332
void LinuxKernelRewriter::processLKSections () {
321
- processLKPCIFixup ();
322
333
processLKKSymtab ();
323
334
processLKKSymtab (true );
324
335
processLKSMPLocks ();
325
336
}
326
337
327
- // / Process .pci_fixup section of Linux Kernel.
328
- // / This section contains a list of entries for different PCI devices and their
329
- // / corresponding hook handler (code pointer where the fixup
330
- // / code resides, usually on x86_64 it is an entry PC relative 32 bit offset).
331
- // / Documentation is in include/linux/pci.h.
332
- void LinuxKernelRewriter::processLKPCIFixup () {
333
- ErrorOr<BinarySection &> SectionOrError =
334
- BC.getUniqueSectionByName (" .pci_fixup" );
335
- if (!SectionOrError)
336
- return ;
337
-
338
- const uint64_t SectionSize = SectionOrError->getSize ();
339
- const uint64_t SectionAddress = SectionOrError->getAddress ();
340
- assert ((SectionSize % 16 ) == 0 && " .pci_fixup size is not a multiple of 16" );
341
-
342
- for (uint64_t I = 12 ; I + 4 <= SectionSize; I += 16 ) {
343
- const uint64_t PC = SectionAddress + I;
344
- ErrorOr<uint64_t > Offset = BC.getSignedValueAtAddress (PC, 4 );
345
- assert (Offset && " cannot read value from .pci_fixup" );
346
- const int32_t SignedOffset = *Offset;
347
- const uint64_t HookupAddress = PC + SignedOffset;
348
- BinaryFunction *HookupFunction =
349
- BC.getBinaryFunctionAtAddress (HookupAddress);
350
- assert (HookupFunction && " expected function for entry in .pci_fixup" );
351
- BC.addRelocation (PC, HookupFunction->getSymbol (), Relocation::getPC32 (), 0 ,
352
- *Offset);
353
- }
354
- }
355
-
356
338
// / Process __ksymtab[_gpl] sections of Linux Kernel.
357
339
// / This section lists all the vmlinux symbols that kernel modules can access.
358
340
// /
@@ -1283,6 +1265,84 @@ Error LinuxKernelRewriter::readAltInstructions() {
1283
1265
return Error::success ();
1284
1266
}
1285
1267
1268
+ // / When the Linux kernel needs to handle an error associated with a given PCI
1269
+ // / device, it uses a table stored in .pci_fixup section to locate a fixup code
1270
+ // / specific to the vendor and the problematic device. The section contains a
1271
+ // / list of the following structures defined in include/linux/pci.h:
1272
+ // /
1273
+ // / struct pci_fixup {
1274
+ // / u16 vendor; /* Or PCI_ANY_ID */
1275
+ // / u16 device; /* Or PCI_ANY_ID */
1276
+ // / u32 class; /* Or PCI_ANY_ID */
1277
+ // / unsigned int class_shift; /* should be 0, 8, 16 */
1278
+ // / int hook_offset;
1279
+ // / };
1280
+ // /
1281
+ // / Normally, the hook will point to a function start and we don't have to
1282
+ // / update the pointer if we are not relocating functions. Hence, while reading
1283
+ // / the table we validate this assumption. If a function has a fixup code in the
1284
+ // / middle of its body, we issue a warning and ignore it.
1285
+ Error LinuxKernelRewriter::readPCIFixupTable () {
1286
+ PCIFixupSection = BC.getUniqueSectionByName (" .pci_fixup" );
1287
+ if (!PCIFixupSection)
1288
+ return Error::success ();
1289
+
1290
+ if (PCIFixupSection->getSize () % PCI_FIXUP_ENTRY_SIZE)
1291
+ return createStringError (errc::executable_format_error,
1292
+ " PCI fixup table size error" );
1293
+
1294
+ const uint64_t Address = PCIFixupSection->getAddress ();
1295
+ DataExtractor DE = DataExtractor (PCIFixupSection->getContents (),
1296
+ BC.AsmInfo ->isLittleEndian (),
1297
+ BC.AsmInfo ->getCodePointerSize ());
1298
+ uint64_t EntryID = 0 ;
1299
+ DataExtractor::Cursor Cursor (0 );
1300
+ while (Cursor && !DE.eof (Cursor)) {
1301
+ const uint16_t Vendor = DE.getU16 (Cursor);
1302
+ const uint16_t Device = DE.getU16 (Cursor);
1303
+ const uint32_t Class = DE.getU32 (Cursor);
1304
+ const uint32_t ClassShift = DE.getU32 (Cursor);
1305
+ const uint64_t HookAddress =
1306
+ Address + Cursor.tell () + (int32_t )DE.getU32 (Cursor);
1307
+
1308
+ if (!Cursor)
1309
+ return createStringError (errc::executable_format_error,
1310
+ " out of bounds while reading .pci_fixup: %s" ,
1311
+ toString (Cursor.takeError ()).c_str ());
1312
+
1313
+ ++EntryID;
1314
+
1315
+ if (opts::DumpPCIFixups) {
1316
+ BC.outs () << " PCI fixup entry: " << EntryID << " \n\t Vendor 0x"
1317
+ << Twine::utohexstr (Vendor) << " \n\t Device: 0x"
1318
+ << Twine::utohexstr (Device) << " \n\t Class: 0x"
1319
+ << Twine::utohexstr (Class) << " \n\t ClassShift: 0x"
1320
+ << Twine::utohexstr (ClassShift) << " \n\t HookAddress: 0x"
1321
+ << Twine::utohexstr (HookAddress) << ' \n ' ;
1322
+ }
1323
+
1324
+ BinaryFunction *BF = BC.getBinaryFunctionContainingAddress (HookAddress);
1325
+ if (!BF && opts::Verbosity) {
1326
+ BC.outs () << " BOLT-INFO: no function matches address 0x"
1327
+ << Twine::utohexstr (HookAddress)
1328
+ << " of hook from .pci_fixup\n " ;
1329
+ }
1330
+
1331
+ if (!BF || !BC.shouldEmit (*BF))
1332
+ continue ;
1333
+
1334
+ if (const uint64_t Offset = HookAddress - BF->getAddress ()) {
1335
+ BC.errs () << " BOLT-WARNING: PCI fixup detected in the middle of function "
1336
+ << *BF << " at offset 0x" << Twine::utohexstr (Offset) << ' \n ' ;
1337
+ BF->setSimple (false );
1338
+ }
1339
+ }
1340
+
1341
+ BC.outs () << " BOLT-INFO: parsed " << EntryID << " PCI fixup entries\n " ;
1342
+
1343
+ return Error::success ();
1344
+ }
1345
+
1286
1346
} // namespace
1287
1347
1288
1348
std::unique_ptr<MetadataRewriter>
0 commit comments