Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

nbritton

macrumors regular
Original poster
May 22, 2008
153
113
Correct me if I'm wrong, but the Intel 5520 chipset datasheet explicitly states that this IOH chipset supports PCIe bifurcation. I'm aware that the MacPro5,1 has no traditional BIOS to speak of, but the datasheet also further states that you can enable bifurcation by merely flipping some register bits via an EFI utility, here is a screenshot of page 519 from said datasheet for MacPro5,1 PCIe slot 2 (device 7 / IOU1):

1765145023172.png

Markdown (GitHub flavored):
# BifurcateIOU1 (MacPro4,1/5,1 / Intel 5520 IOH)

UEFI utility to configure **IOU1 (slot 2)** PCIe lane bifurcation on classic Mac Pro 4,1/5,1.

## Slot / root-port mapping (MacPro4,1/5,1)

- **Slot 1 (x16)** = IOU0 root port **00:03.0**
- **Slot 2 (x16)** = IOU1 root port **00:07.0** (default target)
- Slots 3/4 are behind a PCIe x4 switch (not controlled here)

## IOU1 register

- BDF: 00:07.0
- Offset: 0x190 (PCIE_IOU1_BIF_CTRL)
- bits[2:0] = mode
- bit[3] = start bifurcation (write-1-to-start, reads as 0)

Supported modes:
- x16
- x8x8
- x8x4x4
- x4x4x8
- x4x4x4x4
- strap/default
- raw 0..7 (reserved values included)

## Usage

UEFI shell:
- `BifurcateIOU1.efi --show`
- `BifurcateIOU1.efi --mode x8x8 --reset`
- `BifurcateIOU1.efi --mode x4x4x4x4 --start --poll`

OpenCore / OCLP:
- Put `BifurcateIOU1.efi` in `EFI/OC/Tools/`
- Add it to `Misc -> Tools` in `config.plist`
- You can either run it **interactively** (no args) or set the Tool `Arguments` string
  (this build parses both UEFI shell args and `LoadOptions`).

## Building with edk2

Drop `BifurcateIOU1.c` + `BifurcateIOU1.inf` into an edk2 tree and build:

- `build -a X64 -t GCC5 -p MdeModulePkg/MdeModulePkg.dsc -m <path>/BifurcateIOU1.inf`

The output `.efi` will be in `Build/.../X64/`.

## Safety notes

- For **topology changes**, a **cold reset** (and sometimes a full power cycle) is usually required.
- If you force a split mode and leave a single x16 GPU in slot 2, link training may fail or be unstable.
  Use slot 1 for your GPU while experimenting.

C:
// SPDX-License-Identifier: MIT
//
// BifurcateIOU1.c  (EDK2 UEFI application)
// ------------------------------------------------------------
// Intel 5520/5500 IOH (classic Mac Pro 4,1/5,1) IOU bifurcation helper.
//
// Slot mapping on MacPro4,1/5,1 (per Apple block diagram / typical backplane):
//   Slot 1 (x16) -> IOU0 root port @ 00:03.0
//   Slot 2 (x16) -> IOU1 root port @ 00:07.0   [DEFAULT TARGET OF THIS TOOL]
//   Slots 3/4    -> behind a PCIe x4 switch (not controlled here)
//
// IOU1 bifurcation control register (Intel 5520 IOH):
//   BDF      : 00:07.0
//   Offset   : 0x190  (PCIE_IOU1_BIF_CTRL)
//   bit[3]   : Start Bifurcation (write-1-to-start; always reads 0)
//   bits[2:0]: Mode:
//               100 = x16
//               011 = x8x8
//               010 = x8x4x4
//               001 = x4x4x8
//               000 = x4x4x4x4
//               111 = strap/default
//               101/110 reserved (accessible via --raw)
//
// Notes:
// - For topology changes, you usually need a *cold reset* (and sometimes a full power cycle)
//   for new root ports (e.g., 00:08.0/00:09.0/00:0a.0) to enumerate. Use --reset.
// - If you set a split mode and leave a single x16 GPU in slot 2, link training may fail or
//   fall back unpredictably. Use slot 1 for your GPU when experimenting.
//
// Usage (UEFI Shell / OpenCore Tools / OCLP):
//   BifurcateIOU1.efi --show
//   BifurcateIOU1.efi --mode x8x8 --reset
//   BifurcateIOU1.efi --mode x4x4x4x4 --start --poll
//
// OpenCore note: if ShellParameters is not available, this tool will also parse arguments
// from the loaded image's LoadOptions (the "Arguments" field in OpenCore Misc->Tools).

#include <Uefi.h>

#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>

#include <Protocol/LoadedImage.h>
#include <Protocol/PciRootBridgeIo.h>
#include <Protocol/ShellParameters.h>

#ifndef EFI_PCI_ADDRESS
#define EFI_PCI_ADDRESS(Bus,Dev,Func,Reg) \
  ((UINT64)(Bus) << 24 | (UINT64)(Dev) << 16 | (UINT64)(Func) << 8 | (UINT64)(Reg))
#endif

#define INTEL_VENDOR_ID  0x8086
#define IOU1_BUS_DEFAULT 0
#define IOU1_DEV_DEFAULT 7
#define IOU1_FUN_DEFAULT 0
#define IOU1_BIF_CTRL_OFF 0x190

#define PCI_STATUS_OFFSET 0x06
#define PCI_CAP_PTR_OFFSET 0x34
#define PCI_CAP_ID_EXPRESS 0x10

typedef struct {
  UINT8   Bus;
  UINT8   Dev;
  UINT8   Func;

  UINT8   ModeBits;          // bits[2:0] written into IOU1_BIF_CTRL
  BOOLEAN ModeProvided;      // user specified a mode / raw value

  BOOLEAN Start;             // also write start bit (bit3)
  BOOLEAN DoReset;           // call ResetSystem() after programming
  BOOLEAN Poll;              // poll PCIe Link Status DLLLA after Start
  BOOLEAN Retrain;           // toggle "Retrain Link" in PCIe Link Control (best effort)
  UINTN   PollTimeoutMs;

  BOOLEAN DryRun;
  BOOLEAN Verbose;
  BOOLEAN Force;             // ignore vendor/device sanity checks
  BOOLEAN ShowOnly;
} APP_CONFIG;

STATIC CONST CHAR16 *mModeNames[8] = {
  L"x4x4x4x4 (000b)",
  L"x4x4x8   (001b)",
  L"x8x4x4   (010b)",
  L"x8x8     (011b)",
  L"x16      (100b)",
  L"reserved (101b)",
  L"reserved (110b)",
  L"strap/default (111b)"
};

STATIC
VOID
PrintUsage(VOID)
{
  Print(L"\nBifurcateIOU1 - Intel 5520/5500 IOH IOU1 bifurcation utility\n\n");
  Print(L"Default target BDF: 00:07.0  (IOU1)\n\n");
  Print(L"Options:\n");
  Print(L"  --show                 Print current IOU1_BIF_CTRL and link status, then exit\n");
  Print(L"  --bdf BB:DD.F          Override target BDF (hex). Example: --bdf 00:07.0\n");
  Print(L"  --mode <name>          One of: strap, x16, x8x8, x8x4x4, x4x4x8, x4x4x4x4\n");
  Print(L"  --raw <0..7>           Write raw bits[2:0] (reserved values allowed)\n");
  Print(L"  --start                Also set Start Bifurcation bit (bit3, write-only)\n");
  Print(L"  --poll                 After --start, poll DLLLA in LNKSTS until active\n");
  Print(L"  --timeout <ms>         Poll timeout in ms (default 5000)\n");
  Print(L"  --retrain              Request PCIe link retrain (best effort)\n");
  Print(L"  --reset                Cold reset after programming (recommended for topology changes)\n");
  Print(L"  --dry-run              Don't write anything; just show what would be done\n");
  Print(L"  --force                Skip vendor sanity checks\n");
  Print(L"  --verbose              Extra prints\n");
  Print(L"  --help                 This help\n\n");

  Print(L"Examples:\n");
  Print(L"  BifurcateIOU1.efi --show\n");
  Print(L"  BifurcateIOU1.efi --mode x8x8 --reset\n");
  Print(L"  BifurcateIOU1.efi --mode x8x8 --start --poll --retrain\n\n");
}

STATIC
VOID
ToLowerInPlace(IN OUT CHAR16 *Str)
{
  if (Str == NULL) return;
  for (; *Str != L'\0'; ++Str) {
    if (*Str >= L'A' && *Str <= L'Z') *Str = (CHAR16)(*Str - L'A' + L'a');
  }
}

STATIC
BOOLEAN
StrEqualsNoCase(CONST CHAR16 *A, CONST CHAR16 *B)
{
  if (A == NULL || B == NULL) return FALSE;

  // Simple case-insensitive compare without allocations.
  while (*A != L'\0' && *B != L'\0') {
    CHAR16 Ca = *A;
    CHAR16 Cb = *B;
    if (Ca >= L'A' && Ca <= L'Z') Ca = (CHAR16)(Ca - L'A' + L'a');
    if (Cb >= L'A' && Cb <= L'Z') Cb = (CHAR16)(Cb - L'A' + L'a');
    if (Ca != Cb) return FALSE;
    ++A; ++B;
  }
  return (*A == L'\0' && *B == L'\0');
}

STATIC
BOOLEAN
StartsWithNoCase(CONST CHAR16 *Str, CONST CHAR16 *Prefix)
{
  if (Str == NULL || Prefix == NULL) return FALSE;
  while (*Prefix != L'\0') {
    CHAR16 Cs = *Str;
    CHAR16 Cp = *Prefix;
    if (Cs >= L'A' && Cs <= L'Z') Cs = (CHAR16)(Cs - L'A' + L'a');
    if (Cp >= L'A' && Cp <= L'Z') Cp = (CHAR16)(Cp - L'A' + L'a');
    if (Cs != Cp) return FALSE;
    ++Str; ++Prefix;
  }
  return TRUE;
}

STATIC
BOOLEAN
ParseUintnAuto(CONST CHAR16 *Str, UINTN *OutVal)
{
  if (Str == NULL || OutVal == NULL) return FALSE;

  // Accept 0x... as hex, otherwise accept decimal or bare hex (common in UEFI shell).
  if (StartsWithNoCase(Str, L"0x")) {
    *OutVal = StrHexToUintn(Str);
    return TRUE;
  }

  // Heuristic: if it contains any hex alpha, treat as hex.
  for (CONST CHAR16 *p = Str; *p; ++p) {
    if ((*p >= L'A' && *p <= L'F') || (*p >= L'a' && *p <= L'f')) {
      *OutVal = StrHexToUintn(Str);
      return TRUE;
    }
  }

  // Otherwise treat as decimal.
  *OutVal = StrDecimalToUintn(Str);
  return TRUE;
}

STATIC
BOOLEAN
ParseBdf(CONST CHAR16 *Str, UINT8 *Bus, UINT8 *Dev, UINT8 *Func)
{
  if (Str == NULL || Bus == NULL || Dev == NULL || Func == NULL) return FALSE;

  // Expected: "BB:DD.F" (hex)
  CONST CHAR16 *Colon = NULL;
  CONST CHAR16 *Dot   = NULL;
  for (CONST CHAR16 *p = Str; *p; ++p) {
    if (*p == L':') Colon = p;
    if (*p == L'.') Dot = p;
  }
  if (Colon == NULL || Dot == NULL || Dot < Colon) return FALSE;

  // Copy substrings into small buffers
  CHAR16 BusStr[5] = {0};
  CHAR16 DevStr[5] = {0};
  CHAR16 FunStr[5] = {0};

  UINTN BusLen = (UINTN)(Colon - Str);
  UINTN DevLen = (UINTN)(Dot - (Colon + 1));
  UINTN FunLen = StrLen(Dot + 1);

  if (BusLen == 0 || BusLen >= ARRAY_SIZE(BusStr)) return FALSE;
  if (DevLen == 0 || DevLen >= ARRAY_SIZE(DevStr)) return FALSE;
  if (FunLen == 0 || FunLen >= ARRAY_SIZE(FunStr)) return FALSE;

  CopyMem(BusStr, Str, BusLen * sizeof(CHAR16));
  CopyMem(DevStr, Colon + 1, DevLen * sizeof(CHAR16));
  CopyMem(FunStr, Dot + 1, FunLen * sizeof(CHAR16));

  UINTN B = StrHexToUintn(BusStr);
  UINTN D = StrHexToUintn(DevStr);
  UINTN F = StrHexToUintn(FunStr);

  if (B > 0xFF || D > 0x1F || F > 0x7) return FALSE;

  *Bus  = (UINT8)B;
  *Dev  = (UINT8)D;
  *Func = (UINT8)F;
  return TRUE;
}

STATIC
BOOLEAN
ParseModeName(CONST CHAR16 *ModeStr, UINT8 *OutModeBits)
{
  if (ModeStr == NULL || OutModeBits == NULL) return FALSE;

  // Accept common spellings.
  if (StrEqualsNoCase(ModeStr, L"strap") || StrEqualsNoCase(ModeStr, L"auto") || StrEqualsNoCase(ModeStr, L"default")) {
    *OutModeBits = 7;
    return TRUE;
  }
  if (StrEqualsNoCase(ModeStr, L"x16") || StrEqualsNoCase(ModeStr, L"16")) {
    *OutModeBits = 4;
    return TRUE;
  }
  if (StrEqualsNoCase(ModeStr, L"x8x8") || StrEqualsNoCase(ModeStr, L"8x8")) {
    *OutModeBits = 3;
    return TRUE;
  }
  if (StrEqualsNoCase(ModeStr, L"x8x4x4") || StrEqualsNoCase(ModeStr, L"8x4x4")) {
    *OutModeBits = 2;
    return TRUE;
  }
  if (StrEqualsNoCase(ModeStr, L"x4x4x8") || StrEqualsNoCase(ModeStr, L"4x4x8")) {
    *OutModeBits = 1;
    return TRUE;
  }
  if (StrEqualsNoCase(ModeStr, L"x4x4x4x4") || StrEqualsNoCase(ModeStr, L"4x4x4x4")) {
    *OutModeBits = 0;
    return TRUE;
  }

  return FALSE;
}

STATIC
EFI_STATUS
FindPciRootBridgeForBdf(
  IN  UINT8 Bus,
  IN  UINT8 Dev,
  IN  UINT8 Func,
  OUT EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL **OutPci
  )
{
  if (OutPci == NULL) return EFI_INVALID_PARAMETER;
  *OutPci = NULL;

  EFI_STATUS Status;
  EFI_HANDLE *Handles = NULL;
  UINTN Count = 0;

  Status = gBS->LocateHandleBuffer(
                  ByProtocol,
                  &gEfiPciRootBridgeIoProtocolGuid,
                  NULL,
                  &Count,
                  &Handles
                  );
  if (EFI_ERROR(Status)) return Status;

  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *Found = NULL;

  for (UINTN i = 0; i < Count; i++) {
    EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *Pci = NULL;
    Status = gBS->HandleProtocol(Handles[i], &gEfiPciRootBridgeIoProtocolGuid, (VOID**)&Pci);
    if (EFI_ERROR(Status) || Pci == NULL) continue;

    // If this root bridge can read the target vendor ID (i.e. bus range covers it), pick it.
    UINT16 Vid = 0xFFFF;
    Status = Pci->Pci.Read(Pci, EfiPciWidthUint16, EFI_PCI_ADDRESS(Bus,Dev,Func,0x00), 1, &Vid);
    if (!EFI_ERROR(Status) && Vid != 0xFFFF && Vid != 0x0000) {
      Found = Pci;
      break;
    }
  }

  if (Handles) FreePool(Handles);

  if (Found == NULL) return EFI_NOT_FOUND;
  *OutPci = Found;
  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
ReadConfig16(
  IN  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *Pci,
  IN  UINT8 Bus,
  IN  UINT8 Dev,
  IN  UINT8 Func,
  IN  UINT16 Offset,
  OUT UINT16 *OutVal
  )
{
  if (Pci == NULL || OutVal == NULL) return EFI_INVALID_PARAMETER;
  return Pci->Pci.Read(Pci, EfiPciWidthUint16, EFI_PCI_ADDRESS(Bus,Dev,Func,Offset), 1, OutVal);
}

STATIC
EFI_STATUS
WriteConfig16(
  IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *Pci,
  IN UINT8 Bus,
  IN UINT8 Dev,
  IN UINT8 Func,
  IN UINT16 Offset,
  IN UINT16 Val
  )
{
  if (Pci == NULL) return EFI_INVALID_PARAMETER;
  UINT16 Tmp = Val;
  return Pci->Pci.Write(Pci, EfiPciWidthUint16, EFI_PCI_ADDRESS(Bus,Dev,Func,Offset), 1, &Tmp);
}

STATIC
EFI_STATUS
ReadConfig8(
  IN  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *Pci,
  IN  UINT8 Bus,
  IN  UINT8 Dev,
  IN  UINT8 Func,
  IN  UINT16 Offset,
  OUT UINT8 *OutVal
  )
{
  if (Pci == NULL || OutVal == NULL) return EFI_INVALID_PARAMETER;
  return Pci->Pci.Read(Pci, EfiPciWidthUint8, EFI_PCI_ADDRESS(Bus,Dev,Func,Offset), 1, OutVal);
}

STATIC
EFI_STATUS
FindCapabilityOffset(
  IN  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *Pci,
  IN  UINT8 Bus,
  IN  UINT8 Dev,
  IN  UINT8 Func,
  IN  UINT8 CapId,
  OUT UINT8 *OutCapOffset
  )
{
  if (Pci == NULL || OutCapOffset == NULL) return EFI_INVALID_PARAMETER;
  *OutCapOffset = 0;

  EFI_STATUS Status;
  UINT16 PciStatus = 0;
  Status = ReadConfig16(Pci, Bus, Dev, Func, PCI_STATUS_OFFSET, &PciStatus);
  if (EFI_ERROR(Status)) return Status;

  if ((PciStatus & BIT4) == 0) return EFI_NOT_FOUND; // Capabilities list not present

  UINT8 CapPtr = 0;
  Status = ReadConfig8(Pci, Bus, Dev, Func, PCI_CAP_PTR_OFFSET, &CapPtr);
  if (EFI_ERROR(Status)) return Status;

  // Walk the standard capability list (256-byte config space). Prevent infinite loops.
  for (UINTN i = 0; i < 48 && CapPtr != 0; i++) {
    UINT8 ThisId = 0, NextPtr = 0;
    Status = ReadConfig8(Pci, Bus, Dev, Func, CapPtr, &ThisId);
    if (EFI_ERROR(Status)) return Status;

    if (ThisId == CapId) {
      *OutCapOffset = CapPtr;
      return EFI_SUCCESS;
    }

    Status = ReadConfig8(Pci, Bus, Dev, Func, (UINT16)(CapPtr + 1), &NextPtr);
    if (EFI_ERROR(Status)) return Status;

    CapPtr = NextPtr;
  }

  return EFI_NOT_FOUND;
}

STATIC
VOID
PrintLinkStatusHuman(UINT16 LinkStatus)
{
  UINTN Speed = (UINTN)(LinkStatus & 0x000F);
  UINTN Width = (UINTN)((LinkStatus >> 4) & 0x003F);
  BOOLEAN Dllla = ((LinkStatus & BIT13) != 0);

  CONST CHAR16 *SpeedStr = L"unknown";
  if (Speed == 1) SpeedStr = L"2.5 GT/s (Gen1)";
  else if (Speed == 2) SpeedStr = L"5.0 GT/s (Gen2)";
  else if (Speed == 3) SpeedStr = L"8.0 GT/s (Gen3)";

  Print(L"  LNKSTS: speed=%u (%s), width=x%u, DLLLA=%s\n", Speed, SpeedStr, Width, Dllla ? L"1" : L"0");
}

STATIC
EFI_STATUS
ReadPcieLinkStatus(
  IN  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *Pci,
  IN  UINT8 Bus,
  IN  UINT8 Dev,
  IN  UINT8 Func,
  OUT UINT16 *OutLinkStatus,
  OUT UINT8  *OutPcieCapOff OPTIONAL
  )
{
  if (OutLinkStatus == NULL) return EFI_INVALID_PARAMETER;

  UINT8 CapOff = 0;
  EFI_STATUS Status = FindCapabilityOffset(Pci, Bus, Dev, Func, PCI_CAP_ID_EXPRESS, &CapOff);
  if (EFI_ERROR(Status)) return Status;

  UINT16 LinkStatus = 0;
  Status = ReadConfig16(Pci, Bus, Dev, Func, (UINT16)(CapOff + 0x12), &LinkStatus);
  if (EFI_ERROR(Status)) return Status;

  *OutLinkStatus = LinkStatus;
  if (OutPcieCapOff != NULL) *OutPcieCapOff = CapOff;
  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
RequestLinkRetrain(
  IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *Pci,
  IN UINT8 Bus,
  IN UINT8 Dev,
  IN UINT8 Func
  )
{
  UINT16 LinkCtl = 0;
  UINT8 CapOff = 0;
  EFI_STATUS Status = FindCapabilityOffset(Pci, Bus, Dev, Func, PCI_CAP_ID_EXPRESS, &CapOff);
  if (EFI_ERROR(Status)) return Status;

  Status = ReadConfig16(Pci, Bus, Dev, Func, (UINT16)(CapOff + 0x10), &LinkCtl);
  if (EFI_ERROR(Status)) return Status;

  // Link Control bit 5 = Retrain Link (PCIe base spec)
  Status = WriteConfig16(Pci, Bus, Dev, Func, (UINT16)(CapOff + 0x10), (UINT16)(LinkCtl | BIT5));
  return Status;
}

STATIC
EFI_STATUS
PollDllla(
  IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *Pci,
  IN UINT8 Bus,
  IN UINT8 Dev,
  IN UINT8 Func,
  IN UINTN TimeoutMs
  )
{
  UINTN Elapsed = 0;
  const UINTN StepMs = 20;

  while (Elapsed <= TimeoutMs) {
    UINT16 LinkStatus = 0;
    EFI_STATUS Status = ReadPcieLinkStatus(Pci, Bus, Dev, Func, &LinkStatus, NULL);
    if (EFI_ERROR(Status)) return Status;

    if (LinkStatus & BIT13) return EFI_SUCCESS;

    gBS->Stall((UINTN)(StepMs * 1000));
    Elapsed += StepMs;
  }

  return EFI_TIMEOUT;
}

STATIC
VOID
PrintCurrentState(
  IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *Pci,
  IN CONST APP_CONFIG *Cfg
  )
{
  UINT16 Vid = 0xFFFF, Did = 0xFFFF;
  UINT16 Bif = 0;

  if (Pci == NULL || Cfg == NULL) return;

  if (!EFI_ERROR(ReadConfig16(Pci, Cfg->Bus, Cfg->Dev, Cfg->Func, 0x00, &Vid)) &&
      !EFI_ERROR(ReadConfig16(Pci, Cfg->Bus, Cfg->Dev, Cfg->Func, 0x02, &Did))) {
    Print(L"%02x:%02x.%x VID:DID=%04x:%04x\n", Cfg->Bus, Cfg->Dev, Cfg->Func, Vid, Did);
  }

  if (!EFI_ERROR(ReadConfig16(Pci, Cfg->Bus, Cfg->Dev, Cfg->Func, IOU1_BIF_CTRL_OFF, &Bif))) {
    UINT8 ModeBits = (UINT8)(Bif & 0x7);
    Print(L"  IOU1_BIF_CTRL @0x%03x = 0x%04x  =>  %s\n", IOU1_BIF_CTRL_OFF, Bif, mModeNames[ModeBits]);
    Print(L"  (Start bit is write-only and always reads 0)\n");
  }

  UINT16 LinkStatus = 0;
  if (!EFI_ERROR(ReadPcieLinkStatus(Pci, Cfg->Bus, Cfg->Dev, Cfg->Func, &LinkStatus, NULL))) {
    PrintLinkStatusHuman(LinkStatus);
  } else {
    Print(L"  LNKSTS: <unavailable>\n");
  }
}

STATIC
EFI_STATUS
ApplyBifurcation(
  IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *Pci,
  IN CONST APP_CONFIG *Cfg
  )
{
  if (Pci == NULL || Cfg == NULL) return EFI_INVALID_PARAMETER;

  EFI_STATUS Status;

  UINT16 Vid = 0xFFFF;
  Status = ReadConfig16(Pci, Cfg->Bus, Cfg->Dev, Cfg->Func, 0x00, &Vid);
  if (EFI_ERROR(Status)) return Status;

  if (!Cfg->Force && Vid != INTEL_VENDOR_ID) {
    Print(L"Refusing: %02x:%02x.%x vendor is 0x%04x (expected 0x%04x). Use --force to override.\n",
          Cfg->Bus, Cfg->Dev, Cfg->Func, Vid, INTEL_VENDOR_ID);
    return EFI_SECURITY_VIOLATION;
  }

  UINT16 Before = 0;
  Status = ReadConfig16(Pci, Cfg->Bus, Cfg->Dev, Cfg->Func, IOU1_BIF_CTRL_OFF, &Before);
  if (EFI_ERROR(Status)) return Status;

  UINT16 NewLow = (UINT16)(Cfg->ModeBits & 0x7);
  if (Cfg->Start) NewLow = (UINT16)(NewLow | BIT3);

  UINT16 NewVal = (UINT16)((Before & 0xFFF0) | NewLow);

  Print(L"\nCurrent state:\n");
  PrintCurrentState(Pci, Cfg);

  Print(L"\nProgramming IOU1_BIF_CTRL: 0x%04x -> 0x%04x (%s)%s%s\n",
        Before,
        (UINT16)((Before & 0xFFF0) | (Cfg->ModeBits & 0x7)),  // show value without start bit for readability
        mModeNames[Cfg->ModeBits & 0x7],
        Cfg->Start ? L", start" : L"",
        Cfg->DoReset ? L", reset" : L"");

  if (Cfg->DryRun) {
    Print(L"(dry-run) Not writing.\n");
    return EFI_SUCCESS;
  }

  Status = WriteConfig16(Pci, Cfg->Bus, Cfg->Dev, Cfg->Func, IOU1_BIF_CTRL_OFF, NewVal);
  if (EFI_ERROR(Status)) {
    Print(L"Write failed: %r\n", Status);
    return Status;
  }

  gBS->Stall(200000); // 200ms

  UINT16 After = 0;
  Status = ReadConfig16(Pci, Cfg->Bus, Cfg->Dev, Cfg->Func, IOU1_BIF_CTRL_OFF, &After);
  if (!EFI_ERROR(Status)) {
    Print(L"Readback IOU1_BIF_CTRL = 0x%04x  =>  %s\n", After, mModeNames[(UINT8)(After & 0x7)]);
    Print(L"(Note: Start bit reads as 0)\n");
  }

  if (Cfg->Retrain) {
    EFI_STATUS RStatus = RequestLinkRetrain(Pci, Cfg->Bus, Cfg->Dev, Cfg->Func);
    Print(L"Requested link retrain: %r\n", RStatus);
  }

  if (Cfg->Start && Cfg->Poll) {
    Print(L"Polling LNKSTS.DLLLA for up to %u ms...\n", (UINT32)Cfg->PollTimeoutMs);
    EFI_STATUS PStatus = PollDllla(Pci, Cfg->Bus, Cfg->Dev, Cfg->Func, Cfg->PollTimeoutMs);
    Print(L"Poll result: %r\n", PStatus);

    UINT16 LinkStatus = 0;
    if (!EFI_ERROR(ReadPcieLinkStatus(Pci, Cfg->Bus, Cfg->Dev, Cfg->Func, &LinkStatus, NULL))) {
      PrintLinkStatusHuman(LinkStatus);
    }
  }

  if (Cfg->DoReset) {
    Print(L"\nIssuing cold reset...\n");
    gBS->Stall(500000); // 500ms
    gRT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
    // ResetSystem() should not return.
    Print(L"ResetSystem returned unexpectedly.\n");
  }

  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
InteractiveConfigure(APP_CONFIG *Cfg)
{
  if (Cfg == NULL) return EFI_INVALID_PARAMETER;

  Print(L"\nNo mode specified. Interactive mode:\n");
  Print(L"  [0] %s\n", mModeNames[0]);
  Print(L"  [1] %s\n", mModeNames[1]);
  Print(L"  [2] %s\n", mModeNames[2]);
  Print(L"  [3] %s\n", mModeNames[3]);
  Print(L"  [4] %s\n", mModeNames[4]);
  Print(L"  [5] %s\n", mModeNames[5]);
  Print(L"  [6] %s\n", mModeNames[6]);
  Print(L"  [7] %s\n", mModeNames[7]);
  Print(L"  [Q] Quit\n");
  Print(L"\nSelect mode: ");

  EFI_INPUT_KEY Key;
  while (TRUE) {
    UINTN Index = 0;
    gBS->WaitForEvent(1, &gST->ConIn->WaitForKey, &Index);
    if (!EFI_ERROR(gST->ConIn->ReadKeyStroke(gST->ConIn, &Key))) {
      CHAR16 C = Key.UnicodeChar;
      if (C >= L'0' && C <= L'7') {
        Cfg->ModeBits = (UINT8)(C - L'0');
        Cfg->ModeProvided = TRUE;
        Print(L"%c\n", C);
        break;
      }
      if (C == L'q' || C == L'Q') {
        Print(L"Q\n");
        return EFI_ABORTED;
      }
    }
  }

  Print(L"\nApply method:\n");
  Print(L"  [W] Write only (set bits[2:0] and exit)\n");
  Print(L"  [S] Write + Start (also set bit3)\n");
  Print(L"  [R] Write + Cold Reset (recommended)\n");
  Print(L"  [Q] Quit\n");
  Print(L"\nSelect: ");

  while (TRUE) {
    UINTN Index = 0;
    gBS->WaitForEvent(1, &gST->ConIn->WaitForKey, &Index);
    if (!EFI_ERROR(gST->ConIn->ReadKeyStroke(gST->ConIn, &Key))) {
      CHAR16 C = Key.UnicodeChar;
      if (C == L'w' || C == L'W') {
        Print(L"W\n");
        Cfg->Start = FALSE;
        Cfg->DoReset = FALSE;
        break;
      }
      if (C == L's' || C == L'S') {
        Print(L"S\n");
        Cfg->Start = TRUE;
        Cfg->Poll = TRUE;
        break;
      }
      if (C == L'r' || C == L'R') {
        Print(L"R\n");
        Cfg->DoReset = TRUE;
        break;
      }
      if (C == L'q' || C == L'Q') {
        Print(L"Q\n");
        return EFI_ABORTED;
      }
    }
  }

  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
GetArgvArgcFromShell(
  IN  EFI_HANDLE ImageHandle,
  OUT UINTN *OutArgc,
  OUT CHAR16 ***OutArgv
  )
{
  if (OutArgc == NULL || OutArgv == NULL) return EFI_INVALID_PARAMETER;
  *OutArgc = 0;
  *OutArgv = NULL;

  EFI_SHELL_PARAMETERS_PROTOCOL *ShellParams = NULL;
  EFI_STATUS Status = gBS->OpenProtocol(
                      ImageHandle,
                      &gEfiShellParametersProtocolGuid,
                      (VOID**)&ShellParams,
                      ImageHandle,
                      NULL,
                      EFI_OPEN_PROTOCOL_GET_PROTOCOL
                      );
  if (EFI_ERROR(Status) || ShellParams == NULL) {
    return EFI_NOT_FOUND;
  }

  *OutArgc = ShellParams->Argc;
  *OutArgv = ShellParams->Argv;
 

STATIC
BOOLEAN
IsWhiteSpace(IN CHAR16 C)
{
  return (C == L' ' || C == L'\t' || C == L'\r' || C == L'\n');
}

//
// OpenCore can pass a tool's "Arguments" string via EFI_LOADED_IMAGE_PROTOCOL->LoadOptions.
// That path does *not* install EFI_SHELL_PARAMETERS_PROTOCOL, so we tokenize the LoadOptions
// string into an argv/argc-like form ourselves.
//
STATIC
EFI_STATUS
GetArgvArgcFromLoadOptions(
  IN  EFI_HANDLE ImageHandle,
  OUT UINTN *OutArgc,
  OUT CHAR16 ***OutArgv,
  OUT CHAR16 **OutCmdLineBuffer
  )
{
  if (OutArgc == NULL || OutArgv == NULL || OutCmdLineBuffer == NULL) return EFI_INVALID_PARAMETER;
  *OutArgc = 0;
  *OutArgv = NULL;
  *OutCmdLineBuffer = NULL;

  EFI_LOADED_IMAGE_PROTOCOL *Loaded = NULL;
  EFI_STATUS Status = gBS->HandleProtocol(
                      ImageHandle,
                      &gEfiLoadedImageProtocolGuid,
                      (VOID**)&Loaded
                      );
  if (EFI_ERROR(Status) || Loaded == NULL || Loaded->LoadOptions == NULL || Loaded->LoadOptionsSize < sizeof(CHAR16)) {
    return EFI_NOT_FOUND;
  }

  UINTN CharCount = Loaded->LoadOptionsSize / sizeof(CHAR16);
  CHAR16 *Buf = AllocateZeroPool((CharCount + 1) * sizeof(CHAR16));
  if (Buf == NULL) return EFI_OUT_OF_RESOURCES;

  CopyMem(Buf, Loaded->LoadOptions, Loaded->LoadOptionsSize);
  Buf[CharCount] = L'\0';

  // Quick check: any non-whitespace?
  BOOLEAN HasNonSpace = FALSE;
  for (CHAR16 *p = Buf; *p; ++p) {
    if (!IsWhiteSpace(*p)) { HasNonSpace = TRUE; break; }
  }
  if (!HasNonSpace) {
    FreePool(Buf);
    return EFI_NOT_FOUND;
  }

  UINTN MaxTokens = (StrLen(Buf) / 2) + 2;  // +1 for argv[0]
  CHAR16 **Argv = AllocateZeroPool(MaxTokens * sizeof(CHAR16*));
  if (Argv == NULL) {
    FreePool(Buf);
    return EFI_OUT_OF_RESOURCES;
  }

  UINTN Argc = 0;
  Argv[Argc++] = L"BifurcateIOU1";

  // Tokenize in-place. Supports simple quoted strings ("like this").
  CHAR16 *p = Buf;
  while (*p != L'\0') {
    while (*p != L'\0' && IsWhiteSpace(*p)) {
      *p = L'\0';
      ++p;
    }
    if (*p == L'\0') break;

    if (Argc >= MaxTokens) break;

    if (*p == L'"') {
      ++p;
      Argv[Argc++] = p;
      while (*p != L'\0' && *p != L'"') ++p;
      if (*p == L'"') {
        *p = L'\0';
        ++p;
      }
    } else {
      Argv[Argc++] = p;
      while (*p != L'\0' && !IsWhiteSpace(*p)) ++p;
      if (*p != L'\0') {
        *p = L'\0';
        ++p;
      }
    }
  }

  if (Argc <= 1) {
    FreePool(Argv);
    FreePool(Buf);
    return EFI_NOT_FOUND;
  }

  *OutArgc = Argc;
  *OutArgv = Argv;
  *OutCmdLineBuffer = Buf;
  return EFI_SUCCESS;
}
return EFI_SUCCESS;
}

STATIC
EFI_STATUS
ParseArgs(IN UINTN Argc, IN CHAR16 **Argv, IN OUT APP_CONFIG *Cfg)
{
  if (Cfg == NULL) return EFI_INVALID_PARAMETER;

  for (UINTN i = 1; i < Argc; i++) {
    CHAR16 *Arg = Argv[i];
    if (Arg == NULL) continue;

    if (StrEqualsNoCase(Arg, L"--help") || StrEqualsNoCase(Arg, L"-h") || StrEqualsNoCase(Arg, L"/?")) {
      PrintUsage();
      return EFI_ABORTED;
    }

    if (StrEqualsNoCase(Arg, L"--show")) { Cfg->ShowOnly = TRUE; continue; }
    if (StrEqualsNoCase(Arg, L"--start")) { Cfg->Start = TRUE; continue; }
    if (StrEqualsNoCase(Arg, L"--poll")) { Cfg->Poll = TRUE; continue; }
    if (StrEqualsNoCase(Arg, L"--reset")) { Cfg->DoReset = TRUE; continue; }
    if (StrEqualsNoCase(Arg, L"--dry-run")) { Cfg->DryRun = TRUE; continue; }
    if (StrEqualsNoCase(Arg, L"--verbose")) { Cfg->Verbose = TRUE; continue; }
    if (StrEqualsNoCase(Arg, L"--force")) { Cfg->Force = TRUE; continue; }
    if (StrEqualsNoCase(Arg, L"--retrain")) { Cfg->Retrain = TRUE; continue; }

    if (StartsWithNoCase(Arg, L"--bdf=")) {
      if (!ParseBdf(Arg + 6, &Cfg->Bus, &Cfg->Dev, &Cfg->Func)) {
        Print(L"Bad --bdf value: %s\n", Arg + 6);
        return EFI_INVALID_PARAMETER;
      }
      continue;
    }
    if (StrEqualsNoCase(Arg, L"--bdf")) {
      if (i + 1 >= Argc) return EFI_INVALID_PARAMETER;
      if (!ParseBdf(Argv[++i], &Cfg->Bus, &Cfg->Dev, &Cfg->Func)) {
        Print(L"Bad --bdf value: %s\n", Argv[i]);
        return EFI_INVALID_PARAMETER;
      }
      continue;
    }

    if (StartsWithNoCase(Arg, L"--mode=")) {
      UINT8 Mode = 0;
      if (!ParseModeName(Arg + 7, &Mode)) {
        Print(L"Bad --mode value: %s\n", Arg + 7);
        return EFI_INVALID_PARAMETER;
      }
      Cfg->ModeBits = Mode;
      Cfg->ModeProvided = TRUE;
      continue;
    }
    if (StrEqualsNoCase(Arg, L"--mode")) {
      if (i + 1 >= Argc) return EFI_INVALID_PARAMETER;
      UINT8 Mode = 0;
      if (!ParseModeName(Argv[++i], &Mode)) {
        Print(L"Bad --mode value: %s\n", Argv[i]);
        return EFI_INVALID_PARAMETER;
      }
      Cfg->ModeBits = Mode;
      Cfg->ModeProvided = TRUE;
      continue;
    }

    if (StartsWithNoCase(Arg, L"--raw=")) {
      UINTN V = 0;
      if (!ParseUintnAuto(Arg + 6, &V) || V > 7) {
        Print(L"Bad --raw value: %s (expected 0..7)\n", Arg + 6);
        return EFI_INVALID_PARAMETER;
      }
      Cfg->ModeBits = (UINT8)V;
      Cfg->ModeProvided = TRUE;
      continue;
    }
    if (StrEqualsNoCase(Arg, L"--raw")) {
      if (i + 1 >= Argc) return EFI_INVALID_PARAMETER;
      UINTN V = 0;
      if (!ParseUintnAuto(Argv[++i], &V) || V > 7) {
        Print(L"Bad --raw value: %s (expected 0..7)\n", Argv[i]);
        return EFI_INVALID_PARAMETER;
      }
      Cfg->ModeBits = (UINT8)V;
      Cfg->ModeProvided = TRUE;
      continue;
    }

    if (StartsWithNoCase(Arg, L"--timeout=")) {
      UINTN V = 0;
      if (!ParseUintnAuto(Arg + 10, &V)) return EFI_INVALID_PARAMETER;
      Cfg->PollTimeoutMs = V;
      continue;
    }
    if (StrEqualsNoCase(Arg, L"--timeout")) {
      if (i + 1 >= Argc) return EFI_INVALID_PARAMETER;
      UINTN V = 0;
      if (!ParseUintnAuto(Argv[++i], &V)) return EFI_INVALID_PARAMETER;
      Cfg->PollTimeoutMs = V;
      continue;
    }

    Print(L"Unknown argument: %s\n", Arg);
    return EFI_INVALID_PARAMETER;
  }

  return EFI_SUCCESS;
}

EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
{
  APP_CONFIG Cfg;
  ZeroMem(&Cfg, sizeof(Cfg));
  Cfg.Bus = IOU1_BUS_DEFAULT;
  Cfg.Dev = IOU1_DEV_DEFAULT;
  Cfg.Func = IOU1_FUN_DEFAULT;
  Cfg.PollTimeoutMs = 5000;

  UINTN Argc = 0;
  CHAR16 **Argv = NULL;
  CHAR16 *CmdLineBuf = NULL;
  BOOLEAN ArgvOwned = FALSE;

  // Prefer UEFI Shell argc/argv when present, but also support OpenCore's "Arguments"
  // (passed via EFI_LOADED_IMAGE_PROTOCOL->LoadOptions) when ShellParameters isn't installed.
  EFI_STATUS ArgStatus = GetArgvArgcFromShell(ImageHandle, &Argc, &Argv);
  if (EFI_ERROR(ArgStatus) || Argc <= 1) {
    ArgStatus = GetArgvArgcFromLoadOptions(ImageHandle, &Argc, &Argv, &CmdLineBuf);
    if (!EFI_ERROR(ArgStatus)) {
      ArgvOwned = TRUE;
    }
  }

  if (!EFI_ERROR(ArgStatus) && Argc > 1) {
    EFI_STATUS ParseStatus = ParseArgs(Argc, Argv, &Cfg);

    if (ArgvOwned) {
      if (Argv) FreePool(Argv);
      if (CmdLineBuf) FreePool(CmdLineBuf);
      Argv = NULL;
      CmdLineBuf = NULL;
      ArgvOwned = FALSE;
    }

    if (ParseStatus == EFI_ABORTED) return EFI_SUCCESS;   // help printed
    if (EFI_ERROR(ParseStatus)) {
      Print(L"Argument parse error: %r\n", ParseStatus);
      PrintUsage();
      return ParseStatus;
    }
  }
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *Pci = NULL;
  Status = FindPciRootBridgeForBdf(Cfg.Bus, Cfg.Dev, Cfg.Func, &Pci);
  if (EFI_ERROR(Status)) {
    Print(L"Could not find a PCI root bridge covering %02x:%02x.%x: %r\n", Cfg.Bus, Cfg.Dev, Cfg.Func, Status);
    return Status;
  }

  if (Cfg.ShowOnly) {
    PrintCurrentState(Pci, &Cfg);
    return EFI_SUCCESS;
  }

  if (!Cfg.ModeProvided) {
    Status = InteractiveConfigure(&Cfg);
    if (EFI_ERROR(Status)) return Status;
  }

  Status = ApplyBifurcation(Pci, &Cfg);
  if (EFI_ERROR(Status)) {
    Print(L"Failed: %r\n", Status);
  }
  return Status;
}

Code:
## @file
#  BifurcateIOU1.inf
#
#  Build this module to produce BifurcateIOU1.efi (UEFI application).
#
#  Example build (from edk2 root, after setting up build environment):
#    build -a X64 -t GCC5 -p MdeModulePkg/MdeModulePkg.dsc -m <path>/BifurcateIOU1.inf
#
#  SPDX-License-Identifier: MIT
##

[Defines]
  INF_VERSION                    = 0x0001001A
  BASE_NAME                      = BifurcateIOU1
  FILE_GUID                      = 140B42E6-5AA5-4602-BDDF-07D50D579D0A
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = UefiMain

[Sources]
  BifurcateIOU1.c

[Packages]
  MdePkg/MdePkg.dec
  MdeModulePkg/MdeModulePkg.dec
  ShellPkg/ShellPkg.dec

[LibraryClasses]
  UefiApplicationEntryPoint
  UefiLib
  UefiBootServicesTableLib
  UefiRuntimeServicesTableLib
  BaseLib
  BaseMemoryLib
  MemoryAllocationLib
 
  • Like
Reactions: keksikuningas
Where does the code come from? And what is `build` command mentioned in the .inf file? I suppose it's available after "setting up build environment", whatever that means?

This post is very curious, I am eager to learn more.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.