Newer
Older
AMI-Aptio-BIOS-Reversed / MnpDxe / MnpVlan.c
@Ajax Dong Ajax Dong 2 days ago 23 KB Init
/**
 * @file MnpVlan.c
 *
 * @brief VLAN Configuration Protocol Implementation
 *
 * This file implements the VLAN_CONFIG_PROTOCOL interfaces:
 *   - MnpVlanSet:    Set/Add a VLAN ID and priority pair
 *   - MnpVlanFind:   Find VLAN entries (specific or all)
 *   - MnpVlanRemove: Remove a VLAN ID and priority pair
 *
 * It also provides internal helpers:
 *   - MnpCreateVlanChildHandle: Create a child handle with VLAN config protocol
 *   - MnpParseVlanTag:          Parse a VLAN tag from a received packet
 *   - MnpInsertVlanTag:         Insert/reorder VLAN tag in a packet header
 *   - MnpReadVlanConfiguration: Read the "NetworkStackVar" variable
 *   - MnpFindServiceByVlan:     Walk the service list to find matching VLAN ID
 *
 * Source: AmiNetworkPkg/UefiNetworkStack/Common/MnpDxe/MnpVlan.c
 */

#include "MnpDxe.h"

//
// External globals
//
extern EFI_BOOT_SERVICES    *gBS;
extern EFI_RUNTIME_SERVICES *gRT;
extern VOID                 *gDpcProtocol;
extern UINT64                gImageHandle;

/* ====================================================================== */
/*  MnpParseVlanTag                                                        */
/* ====================================================================== */

/**
 * Parse a VLAN tag from a received packet.
 *
 * Scans the packet data at the expected VLAN tag position (based on the
 * underlying SNP's MAC header size) and checks for VLAN_TPID (0x8100).
 * If found, the VLAN ID is extracted, the tag is removed from the data
 * (shrinking the packet by 4 bytes), and the function returns TRUE.
 *
 * @param[in]  MnpDeviceData  Pointer to MNP_DEVICE_DATA.
 * @param[in]  Nbuf           Packet buffer to parse (NET_BUF, at offset +0x38
 *                            for block op protocol pointer).
 * @param[out] VlanId         On success, the 12-bit VLAN ID from the tag.
 *
 * @retval 1   VLAN tag found and parsed.
 * @retval 0   No VLAN tag present (TPID did not match 0x8100).
 */
UINT8
MnpParseVlanTag (
  IN  MNP_DEVICE_DATA  *MnpDeviceData,
  IN  NET_BUF          *Nbuf,
  OUT UINT16           *VlanId
  )
{
  UINT16  Count;
  UINT8  *PacketData;

  //
  // Calculate the byte offset where the VLAN tag would be in the MAC header.
  // Snp->Mode->MediaHeaderSize * 2 gives the position (offset into the frame).
  //
  Count = (UINT16)(2 * MnpDeviceData->SnpModeData->MediaHeaderSize);

  //
  // Get a pointer to the packet data at offset 0 (the raw frame buffer).
  // The block-op protocol inside Nbuf at offset +0x38 can access the data.
  //
  PacketData = (UINT8 *)NetbufGetRawData (Nbuf);

  if (PacketData == NULL) {
    DEBUG ((DEBUG_ERROR, "MnpParseVlanTag: Packet data is NULL\n"));
    return 0;
  }

  //
  // Read the TPID at the Ethernet VLAN tag position (big-endian).
  //
  if (SwapBytes16 (*(UINT16 *)&PacketData[Count]) != VLAN_TPID) {
    *VlanId = 0;
    return 0;
  }

  //
  // Extract the 12-bit VLAN ID from the TCI field (at Count+2, big-endian).
  //
  *VlanId = SwapBytes16 (*(UINT16 *)&PacketData[Count + 2]) & 0x0FFF;

  //
  // Remove the 4-byte VLAN tag from the packet: shift the data after
  // the tag (the actual L3 header) backward by 4 bytes.
  //
  CopyMem (PacketData + 4, PacketData, Count);

  //
  // Trim 4 bytes from the front of the buffer.
  //
  NetbufTrim (Nbuf, 4, NET_BUF_TRIM_HEAD);

  return 1;
}

/* ====================================================================== */
/*  MnpInsertVlanTag                                                        */
/* ====================================================================== */

/**
 * Insert or update a VLAN tag in the MAC header of a packet.
 *
 * For untagged packets (no existing VLAN), the source MAC address is
 * shifted to make room for the 4-byte TPID/TCI tag.  For already-tagged
 * packets, only the tag is refreshed.  The VLAN ID is taken from the
 * MnpServiceData and the VLAN_TPID (0x8100) is written in big-endian.
 *
 * @param[in]      MnpServiceData  Pointer to MNP_SERVICE_DATA (VlanId and
 *                                 Priority fields used).
 * @param[in]      Packet          Opaque packet context (contains MAC header
 *                                 buffer).
 * @param[in,out]  MacHeader       Pointer to current MAC header start.
 * @param[in,out]  HeaderLen       Total header length (in/out).
 */
VOID
MnpInsertVlanTag (
  IN     MNP_SERVICE_DATA  *MnpServiceData,
  IN     VOID              *Packet,
  IN OUT UINT8            **MacHeader,
  IN OUT UINT32           *HeaderLen
  )
{
  MNP_DEVICE_DATA  *MnpDeviceData;
  UINT8            *NewMacHeader;
  UINT32            MediaHeaderLen;

  MnpDeviceData = MnpServiceData->MnpDeviceData;
  MediaHeaderLen = MnpDeviceData->SnpModeData->MediaHeaderSize;

  *MacHeader -= 4;
  *HeaderLen += 4;

  NewMacHeader = *MacHeader;

  //
  // Check if the packet already has a VLAN tag by inspecting the
  // 2-byte field at the expected position (EtherType/LEN offset).
  //
  if (*(UINT16 *)(*MacHeader + MediaHeaderLen) != 0) {
    //
    // Already tagged: shift the source MAC + EtherType right by 2 bytes
    // to accommodate a new TPID, then restore the source MAC.
    //
    CopyMem (NewMacHeader, NewMacHeader + 4, MediaHeaderLen - 2);
    *(UINT16 *)&NewMacHeader[MediaHeaderLen - 2] = 0x0081;  // VLAN_TPID (little-endian: 0x8100)
  } else {
    //
    // Untagged: shift the source MAC area right by 4 bytes and write
    // the VLAN TPID in big-endian.
    //
    *(UINT16 *)&NewMacHeader[MediaHeaderLen + 2] = SwapBytes16 (
      *(UINT16 *)(MnpServiceData->MnpDeviceData->SnpModeData->CurrentAddress.Addr)
      );
  }

  //
  // Write TCI: combine priority (3 bits) + VLAN ID (12 bits).
  //
  *(UINT16 *)&NewMacHeader[MediaHeaderLen] =
    (*(UINT16 *)&NewMacHeader[MediaHeaderLen] & 0xE000) |
    (MnpServiceData->VlanId & 0x0FFF);

  //
  // Store the final TCI value in big-endian.
  //
  *(UINT16 *)&NewMacHeader[MediaHeaderLen] = SwapBytes16 (
    (*(UINT16 *)&NewMacHeader[MediaHeaderLen] & 0x1FFF) |
    ((UINT16)MnpServiceData->Priority << 13)
    );
}

/* ====================================================================== */
/*  MnpReadVlanConfiguration                                               */
/* ====================================================================== */

/**
 * Read the "NetworkStackVar" UEFI variable into an allocated buffer.
 *
 * This variable stores the list of configured VLAN ID / Priority pairs.
 * Each entry is a 16-bit value: priority in the upper 3 bits, VLAN ID in
 * the lower 12 bits (bit 15 is a flag, bit 12-14 = priority).
 *
 * @param[in]      MnpDeviceData  Pointer to MNP_DEVICE_DATA (ControllerHandle
 *                                is used for the variable access).
 * @param[out]     NumberOfVlan   Number of VLAN entries in the returned buffer.
 * @param[out]     VlanBuffer     Allocated buffer containing the VLAN entries,
 *                                or NULL on failure.  Caller must free.
 *
 * @retval EFI_SUCCESS           Variable read successfully.
 * @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
 * @retval EFI_NOT_FOUND         Variable does not exist.
 */
EFI_STATUS
MnpReadVlanConfiguration (
  IN  MNP_DEVICE_DATA  *MnpDeviceData,
  OUT UINT16           *NumberOfVlan,
  OUT UINT8          **VlanBuffer
  )
{
  UINT64      VariableSize;
  UINT8      *Buffer;
  EFI_STATUS  Status;

  //
  // 1. Query the required buffer size (expected to return EFI_BUFFER_TOO_SMALL).
  //
  VariableSize = 0;
  *NumberOfVlan = 0;
  *VlanBuffer = NULL;

  Status = gRT->GetVariable (
                  L"NetworkStackVar",
                  &gNetworkStackVarGuid,
                  NULL,
                  &VariableSize,
                  NULL
                  );

  if (Status != EFI_BUFFER_TOO_SMALL) {
    return EFI_NOT_FOUND;
  }

  //
  // 2. Allocate buffer of the required size.
  //
  Buffer = (UINT8 *)AllocatePool ((UINTN)VariableSize);
  if (Buffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // 3. Read the variable contents.
  //
  Status = gRT->GetVariable (
                  L"NetworkStackVar",
                  &gNetworkStackVarGuid,
                  NULL,
                  &VariableSize,
                  Buffer
                  );

  if (EFI_ERROR (Status)) {
    FreePool (Buffer);
    return Status;
  }

  //
  // 4. Return the count (each entry is 2 bytes, so count = size / 2).
  //
  *NumberOfVlan = (UINT16)(VariableSize >> 1);
  *VlanBuffer = Buffer;

  return EFI_SUCCESS;
}

/* ====================================================================== */
/*  MnpCreateVlanChildHandle                                                */
/* ====================================================================== */

/**
 * Create a child handle for a VLAN-tagged MNP service and install the
 * VLAN_CONFIG_PROTOCOL on it.
 *
 * 1. Opens the VLAN_CONFIG_PROTOCOL on the controller handle to find
 *    existing VLAN children.
 * 2. Builds a MAC address with the VLAN TCI bytes (VLAN ID in Addr[0..1]).
 * 3. Calls MnpCreateChildWithMac to create a new child device path with the
 *    MAC address appended.
 * 4. Installs the VLAN_CONFIG_PROTOCOL (as a protocol interface) on the new
 *    child handle.
 *
 * @param[in]  ImageHandle      The driver's image handle.
 * @param[in]  ControllerHandle The physical NIC controller handle.
 * @param[in]  VlanId           The VLAN ID to associate.
 * @param[out] ChildHandle      On success, the new child handle.
 *
 * @retval Handle of the new child, or 0 on failure.
 */
EFI_HANDLE
MnpCreateVlanChildHandle (
  IN  EFI_HANDLE   ImageHandle,
  IN  EFI_HANDLE   ControllerHandle,
  IN  UINT16       VlanId,
  OUT EFI_HANDLE  *ChildHandle       OPTIONAL
  )
{
  EFI_STATUS                   Status;
  VLAN_CONFIG_PROTOCOL        *VlanConfig;
  UINT8                        DstBuf[6];
  UINT8                        VlanTci[2];
  EFI_HANDLE                   NewHandle;
  EFI_DEVICE_PATH_PROTOCOL    *VlanDevicePath;

  //
  // 1. Open the VLAN_CONFIG_PROTOCOL on the ControllerHandle (by agent).
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gVlanConfigProtocolGuid,
                  (VOID **)&VlanConfig,
                  ImageHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_BY_AGENT
                  );

  if (EFI_ERROR (Status)) {
    return 0;
  }

  //
  // 2. Build a MAC address with the VLAN TCI embedded.
  //    Copy from the hardcoded template (src_3 at 0xA9B0 in binary).
  //
  CopyMem (DstBuf, &gMnpVlanTemplateMac, sizeof (DstBuf));

  //
  // 3. Build a device path containing the MAC address + VLAN TCI.
  //
  VlanTci[0] = (UINT8)(VlanId & 0xFF);
  VlanTci[1] = (UINT8)((VlanId >> 8) & 0xFF);
  VlanDevicePath = MnpCreateDevicePathWithMac (
                     VlanConfig,
                     DstBuf
                     );

  if (VlanDevicePath == NULL) {
    return 0;
  }

  //
  // 4. Install the VLAN_CONFIG_PROTOCOL on a new handle with the device path.
  //
  NewHandle = 0;
  Status = gBS->InstallProtocolInterface (
                  &NewHandle,
                  &gVlanConfigProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  VlanDevicePath
                  );

  if (EFI_ERROR (Status)) {
    FreePool (VlanDevicePath);
    return 0;
  }

  if (ChildHandle != NULL) {
    *ChildHandle = NewHandle;
  }

  return NewHandle;
}

/* ====================================================================== */
/*  MnpVlanSet                                                             */
/* ====================================================================== */

/**
 * Set/add a VLAN configuration entry.
 *
 * This is the VLAN_CONFIG_PROTOCOL.Set() implementation.
 * It validates inputs, finds or creates an MNP_SERVICE_DATA for the given
 * VLAN ID, stores the priority, updates the VLAN configuration variable,
 * and optionally starts the service.
 *
 * @param[in] This      VLAN_CONFIG_PROTOCOL instance pointer.
 * @param[in] VlanId    VLAN identifier (0-4094, 0xFFF is reserved).
 * @param[in] Priority  VLAN priority (0-7).
 *
 * @retval EFI_SUCCESS           VLAN entry added/updated.
 * @retval EFI_INVALID_PARAMETER This is NULL, VlanId > 0xFFE, or Priority > 7.
 * @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
 */
EFI_STATUS
EFIAPI
MnpVlanSet (
  IN VLAN_CONFIG_PROTOCOL  *This,
  IN UINT16                 VlanId,
  IN UINT8                  Priority
  )
{
  MNP_DEVICE_DATA     *MnpDeviceData;
  MNP_SERVICE_DATA    *MnpServiceData;
  UINTN                Index;
  UINT8                IsNewService;
  UINT16               NumberOfVlan;
  UINT8               *VlanBuffer;
  UINT8               *NewBuffer;
  EFI_STATUS           Status;

  if (This == NULL || VlanId > 0xFFE || Priority > 7) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // CR: recover MNP_DEVICE_DATA from the VLAN_CONFIG_PROTOCOL pointer.
  // The offset from the protocol interface to the MNP_DEVICE_DATA structure
  // is -24 bytes (VlanConfigProtocol is at +0x18 from MnpDeviceData base).
  //
  MnpDeviceData = CR (This, MNP_DEVICE_DATA, VlanConfigProtocol, MNP_DEVICE_DATA_SIGNATURE);

  IsNewService = 0;

  //
  // Check if we already have a service data entry for this VLAN ID.
  //
  MnpServiceData = MnpFindServiceByVlanId (MnpDeviceData, VlanId);

  if (MnpServiceData != NULL) {
    //
    // Service already exists; just update priority.
    //
    MnpServiceData->Priority = Priority;
    MnpServiceData->VlanId = VlanId;
    goto UpdateVariable;
  }

  //
  // New VLAN: need to create a new MNP_SERVICE_DATA.
  // First, if this is the first VLAN (no default untagged service), ensure
  // the untagged (VlanId=0) service is created and started.
  //
  IsNewService = 1;

  if (MnpDeviceData->MnpServiceSnp == NULL) {
    //
    // No services exist yet. Create the default (VlanId=0) service first
    // if we are adding a non-zero VLAN.
    //
    if (VlanId != 0) {
      MnpServiceData = MnpFindServiceByVlanId (MnpDeviceData, 0);
      if (MnpServiceData == NULL) {
        MnpServiceData = MnpCreateServiceData (MnpDeviceData, 0, 0);
        if (MnpServiceData == NULL) {
          return EFI_OUT_OF_RESOURCES;
        }
      }
      //
      // Start the default service.
      //
      Status = MnpStartService (MnpServiceData);
      if (EFI_ERROR (Status)) {
        return Status;
      }
    }
  } else {
    //
    // Default service exists; start it if not already running.
    //
    Status = MnpStartService ((MNP_SERVICE_DATA *)MnpDeviceData->MnpServiceSnp);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }

  //
  // Now create the new service data for this VLAN.
  //
  MnpServiceData = MnpCreateServiceData (MnpDeviceData, VlanId, Priority);
  if (MnpServiceData == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

UpdateVariable:
  //
  // Rebuild the "NetworkStackVar" variable with all current VLAN entries.
  // Walk the service list and serialize VlanId + Priority for each entry.
  //
  NumberOfVlan = (UINT16)MnpDeviceData->NumChildren;
  NewBuffer = NULL;

  Status = MnpReadVlanConfiguration (MnpDeviceData, &NumberOfVlan, &VlanBuffer);
  if (EFI_ERROR (Status)) {
    VlanBuffer = NULL;
    NumberOfVlan = 0;
  }

  //
  // Allocate buffer for the updated list (old count + 1, or just old count
  // if this service already existed).
  //
  NewBuffer = (UINT8 *)AllocatePool ((UINTN)((NumberOfVlan + 1) * 2));
  if (NewBuffer == NULL) {
    if (VlanBuffer != NULL) {
      FreePool (VlanBuffer);
    }
    return EFI_OUT_OF_RESOURCES;
  }

  if (VlanBuffer != NULL) {
    CopyMem (NewBuffer, VlanBuffer, (UINTN)(NumberOfVlan * 2));
    //
    // Update the existing entry or append at the right position.
    //
    for (Index = 0; Index < NumberOfVlan; Index++) {
      if (((UINT16 *)VlanBuffer)[Index] == VlanId) {
        break;
      }
    }
  } else {
    Index = NumberOfVlan;
  }

  if (!IsNewService && Index < NumberOfVlan) {
    //
    // Existing entry: update in-place.
    //
    ((UINT16 *)NewBuffer)[Index] &= 0x1000;
    ((UINT16 *)NewBuffer)[Index] |= (VlanId & 0x0FFF) | ((UINT16)Priority << 13);
  } else {
    //
    // New entry: append.
    //
    ((UINT16 *)NewBuffer)[NumberOfVlan] = (VlanId & 0x0FFF) | ((UINT16)Priority << 13);
    NumberOfVlan++;
  }

  Status = gRT->SetVariable (
                  L"NetworkStackVar",
                  &gNetworkStackVarGuid,
                  EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
                  2 * NumberOfVlan,
                  NewBuffer
                  );

  FreePool (NewBuffer);
  if (VlanBuffer != NULL) {
    FreePool (VlanBuffer);
  }

  return Status;
}

/* ====================================================================== */
/*  MnpVlanFind                                                            */
/* ====================================================================== */

/**
 * Find VLAN configuration entries.
 *
 * Implementation of VLAN_CONFIG_PROTOCOL.Find().
 * If VlanId is non-NULL, find that specific VLAN and return its priority.
 * If VlanId is NULL, return the count and a buffer of all VLAN entries.
 *
 * @param[in]      This     VLAN_CONFIG_PROTOCOL instance pointer.
 * @param[in]      VlanId   Optional VLAN ID to search for.
 * @param[out]     Number   Number of found entries.
 * @param[out]     Buffer   Allocated buffer of VLAN entries (each 4 bytes:
 *                          VlanId + Priority).
 *
 * @retval EFI_SUCCESS           Query completed.
 * @retval EFI_INVALID_PARAMETER This is NULL, or Number/Buffer is NULL.
 * @retval EFI_NOT_FOUND         Specified VlanId not found, or no VLANs.
 */
EFI_STATUS
EFIAPI
MnpVlanFind (
  IN  VLAN_CONFIG_PROTOCOL  *This,
  IN  UINT16                 VlanId       OPTIONAL,
  OUT UINT16                *Number,
  OUT VOID                 **Buffer
  )
{
  MNP_DEVICE_DATA     *MnpDeviceData;
  MNP_SERVICE_DATA    *MnpServiceData;
  MNP_SERVICE_DATA    *Entry;
  LIST_ENTRY          *ListHead;
  LIST_ENTRY          *ListEntry;
  UINT8               *Buf;
  UINTN                Count;

  if (This == NULL || Number == NULL || Buffer == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  *Number = 0;
  *Buffer = NULL;

  MnpDeviceData = CR (This, MNP_DEVICE_DATA, VlanConfigProtocol, MNP_DEVICE_DATA_SIGNATURE);

  if (MnpDeviceData->NumChildren == 0) {
    return EFI_NOT_FOUND;
  }

  if (VlanId > 0) {
    //
    // Specific VLAN lookup.
    //
    MnpServiceData = MnpFindServiceByVlanId (MnpDeviceData, VlanId);
    if (MnpServiceData == NULL) {
      return EFI_NOT_FOUND;
    }

    Buf = (UINT8 *)AllocatePool (4);
    if (Buf == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }

    *(UINT16 *)Buf = MnpServiceData->VlanId;
    *(UINT8 *)(Buf + 2) = MnpServiceData->Priority;

    *Number = 1;
    *Buffer = Buf;

    return EFI_SUCCESS;
  }

  //
  // Enumerate all VLANS.
  //
  ListHead = &MnpDeviceData->ServiceList;
  Count = 0;

  //
  // Walk the service list once to count.
  //
  for (ListEntry = ListHead->ForwardLink;
       ListEntry != ListHead;
       ListEntry = ListEntry->ForwardLink)
  {
    Count++;
  }

  if (Count == 0) {
    return EFI_NOT_FOUND;
  }

  Buf = (UINT8 *)AllocatePool ((UINTN)(Count * 4));
  if (Buf == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  Count = 0;
  for (ListEntry = ListHead->ForwardLink;
       ListEntry != ListHead;
       ListEntry = ListEntry->ForwardLink)
  {
    Entry = CR (ListEntry, MNP_SERVICE_DATA, Link, MNP_SERVICE_DATA_SIGNATURE);
    *(UINT16 *)&Buf[Count * 4] = Entry->VlanId;
    *(UINT8 *)&Buf[Count * 4 + 2] = Entry->Priority;
    Count++;
  }

  *Number = (UINT16)Count;
  *Buffer = Buf;

  return EFI_SUCCESS;
}

/* ====================================================================== */
/*  MnpVlanRemove                                                          */
/* ====================================================================== */

/**
 * Remove a VLAN configuration entry and its associated MNP_SERVICE_DATA.
 *
 * Implementation of VLAN_CONFIG_PROTOCOL.Remove().
 * Finds the service data by VLAN ID, decrements the refcount, destroys
 * the service (if last VLAN entry), and updates the NetworkStackVar
 * variable.
 *
 * @param[in] This    VLAN_CONFIG_PROTOCOL instance pointer.
 * @param[in] VlanId  VLAN identifier to remove (0 is not allowed for
 *                    removal; the default untagged service is kept).
 *
 * @retval EFI_SUCCESS           VLAN entry removed.
 * @retval EFI_INVALID_PARAMETER This is NULL, or VlanId > 0xFFE.
 * @retval EFI_NOT_FOUND         VLAN ID not configured.
 */
EFI_STATUS
EFIAPI
MnpVlanRemove (
  IN VLAN_CONFIG_PROTOCOL  *This,
  IN UINT16                 VlanId
  )
{
  MNP_DEVICE_DATA   *MnpDeviceData;
  MNP_SERVICE_DATA  *MnpServiceData;
  UINT16             NumberOfVlan;
  UINT8             *VlanBuffer;
  UINT8             *NewBuffer;
  UINTN              Index;
  EFI_STATUS         Status;
  MNP_SERVICE_DATA  *Entry;
  LIST_ENTRY        *ListEntry;

  if (This == NULL || VlanId > 0xFFE) {
    return EFI_INVALID_PARAMETER;
  }

  MnpDeviceData = CR (This, MNP_DEVICE_DATA, VlanConfigProtocol, MNP_DEVICE_DATA_SIGNATURE);

  if (MnpDeviceData->MnpServiceSnp == NULL) {
    return EFI_NOT_FOUND;
  }

  //
  // Find the service data for this VLAN.
  //
  MnpServiceData = MnpFindServiceByVlanId (MnpDeviceData, VlanId);
  if (MnpServiceData == NULL) {
    return EFI_NOT_FOUND;
  }

  //
  // Decrement the child service count.
  //
  MnpDeviceData->NumChildren--;

  //
  // If there are other non-zero VLAN services, or the zero-VLAN default
  // still exists, we only need to stop this one VLAN service.
  //
  if (VlanId != 0 || MnpDeviceData->NumChildren > 0) {
    Status = MnpStopService (MnpServiceData);
    if (EFI_ERROR (Status)) {
      return Status;
    }
    Status = MnpDestroyServiceData (MnpServiceData);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }

  //
  // Rebuild the NetworkStackVar variable without this VLAN entry.
  //
  if (MnpDeviceData->NumChildren > 0) {
    NewBuffer = (UINT8 *)AllocatePool ((UINTN)(MnpDeviceData->NumChildren * 2));
    if (NewBuffer == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }

    Index = 0;
    for (ListEntry = MnpDeviceData->ServiceList.ForwardLink;
         ListEntry != &MnpDeviceData->ServiceList;
         ListEntry = ListEntry->ForwardLink)
    {
      Entry = CR (ListEntry, MNP_SERVICE_DATA, Link, MNP_SERVICE_DATA_SIGNATURE);
      *(UINT16 *)&NewBuffer[Index * 2] &= 0x1000;
      *(UINT16 *)&NewBuffer[Index * 2] |= (Entry->VlanId & 0x0FFF) | ((UINT16)Entry->Priority << 13);
      Index++;
    }

    Status = gRT->SetVariable (
                    L"NetworkStackVar",
                    &gNetworkStackVarGuid,
                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
                    (UINTN)(MnpDeviceData->NumChildren * 2),
                    NewBuffer
                    );

    FreePool (NewBuffer);
  } else {
    //
    // No VLANs left: delete the variable.
    //
    Status = gRT->SetVariable (
                    L"NetworkStackVar",
                    &gNetworkStackVarGuid,
                    0,
                    0,
                    NULL
                    );
  }

  return Status;
}

/* ====================================================================== */
/*  MnpFindServiceByVlanId (internal helper)                                */
/* ====================================================================== */

/**
 * Iterate the service list and return the MNP_SERVICE_DATA matching
 * the given VLAN ID.
 *
 * @param[in] MnpDeviceData  The device data whose ServiceList to walk.
 * @param[in] VlanId         12-bit VLAN ID to match.
 *
 * @return Pointer to the matching MNP_SERVICE_DATA, or NULL.
 */
MNP_SERVICE_DATA *
MnpFindServiceByVlanId (
  IN MNP_DEVICE_DATA  *MnpDeviceData,
  IN UINT16            VlanId
  )
{
  LIST_ENTRY       *ListEntry;
  MNP_SERVICE_DATA *Service;

  for (ListEntry = MnpDeviceData->ServiceList.ForwardLink;
       ListEntry != &MnpDeviceData->ServiceList;
       ListEntry = ListEntry->ForwardLink)
  {
    Service = CR (ListEntry, MNP_SERVICE_DATA, Link, MNP_SERVICE_DATA_SIGNATURE);
    if (Service->VlanId == VlanId) {
      return Service;
    }
  }

  return NULL;
}