1299 lines
36 KiB
C
1299 lines
36 KiB
C
/*++
|
||
|
||
Copyright (c) 1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
helper.c
|
||
|
||
Abstract:
|
||
|
||
Helper functions for the loader.
|
||
|
||
Author:
|
||
|
||
Adam Barr (adamba) Aug 29, 1997
|
||
|
||
Revision History:
|
||
|
||
Who When What
|
||
-------- -------- ----------------------------------------------
|
||
adamba 08-29-97 created
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
#include <pxe_cmn.h>
|
||
#include <pxe_api.h>
|
||
#include <undi_api.h>
|
||
#include <ntexapi.h>
|
||
|
||
#ifdef EFI
|
||
#define BINL_PORT 0x0FAB // 4011 (decimal) in little-endian
|
||
#else
|
||
#define BINL_PORT 0xAB0F // 4011 (decimal) in big-endian
|
||
#endif
|
||
|
||
//
|
||
// This removes macro redefinitions which appear because we define __RPC_DOS__,
|
||
// but rpc.h defines __RPC_WIN32__
|
||
//
|
||
|
||
#pragma warning(disable:4005)
|
||
|
||
//
|
||
// As of 12/17/98, SECURITY_DOS is *not* defined - adamba
|
||
//
|
||
|
||
#if defined(SECURITY_DOS)
|
||
//
|
||
// These appear because we defined SECURITY_DOS
|
||
//
|
||
|
||
#define __far
|
||
#define __pascal
|
||
#define __loadds
|
||
#endif
|
||
|
||
#include <security.h>
|
||
#include <rpc.h>
|
||
#include <spseal.h>
|
||
|
||
#ifdef EFI
|
||
#include "bldr.h"
|
||
#include "efi.h"
|
||
#include "efip.h"
|
||
#include "bldria64.h"
|
||
#include "extern.h"
|
||
#endif
|
||
|
||
#if defined(SECURITY_DOS)
|
||
//
|
||
// PSECURITY_STRING is not supposed to be used when SECURITY_DOS is
|
||
// defined -- it should be a WCHAR*. Unfortunately ntlmsp.h breaks
|
||
// this rule and even uses the SECURITY_STRING structure, which there
|
||
// is really no equivalent for in 16-bit mode.
|
||
//
|
||
|
||
typedef SEC_WCHAR * SECURITY_STRING; // more-or-less the intention where it is used
|
||
typedef SEC_WCHAR * PSECURITY_STRING;
|
||
#endif
|
||
|
||
#include <ntlmsp.h>
|
||
|
||
|
||
extern ULONG TftpSecurityHandle;
|
||
extern CtxtHandle TftpClientContextHandle;
|
||
extern BOOLEAN TftpClientContextHandleValid;
|
||
|
||
//
|
||
// From conn.c.
|
||
//
|
||
|
||
ULONG
|
||
ConnItoa (
|
||
IN ULONG Value,
|
||
OUT PUCHAR Buffer
|
||
);
|
||
|
||
#if defined(REMOTE_BOOT_SECURITY)
|
||
ULONG
|
||
ConnAtosign (
|
||
IN PUCHAR Buffer,
|
||
IN ULONG SignLength,
|
||
OUT PUCHAR Sign
|
||
);
|
||
#endif // defined(REMOTE_BOOT_SECURITY)
|
||
|
||
ULONG
|
||
ConnSafeAtol (
|
||
IN PUCHAR Buffer,
|
||
IN PUCHAR BufferEnd
|
||
);
|
||
|
||
// for now, we pull the hack mac list and code so that we only support new ROMs
|
||
|
||
#ifdef EFI
|
||
|
||
|
||
#pragma pack(1)
|
||
typedef struct {
|
||
UINT16 VendorId;
|
||
UINT16 DeviceId;
|
||
UINT16 Command;
|
||
UINT16 Status;
|
||
UINT8 RevisionID;
|
||
UINT8 ClassCode[3];
|
||
UINT8 CacheLineSize;
|
||
UINT8 LaytencyTimer;
|
||
UINT8 HeaderType;
|
||
UINT8 BIST;
|
||
} PCI_DEVICE_INDEPENDENT_REGION;
|
||
|
||
typedef struct {
|
||
UINT32 Bar[6];
|
||
UINT32 CISPtr;
|
||
UINT16 SubsystemVendorID;
|
||
UINT16 SubsystemID;
|
||
UINT32 ExpansionRomBar;
|
||
UINT32 Reserved[2];
|
||
UINT8 InterruptLine;
|
||
UINT8 InterruptPin;
|
||
UINT8 MinGnt;
|
||
UINT8 MaxLat;
|
||
} PCI_DEVICE_HEADER_TYPE_REGION;
|
||
|
||
typedef struct {
|
||
PCI_DEVICE_INDEPENDENT_REGION Hdr;
|
||
PCI_DEVICE_HEADER_TYPE_REGION Device;
|
||
} PCI_TYPE00;
|
||
|
||
|
||
|
||
NTSTATUS
|
||
NetQueryCardInfo(
|
||
IN OUT PNET_CARD_INFO CardInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine queries the ROM for information about the card.
|
||
|
||
Arguments:
|
||
|
||
CardInfo - returns the structure defining the card.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS
|
||
STATUS_UNSUCCESSFUL
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
EFI_STATUS Status = EFI_UNSUPPORTED;
|
||
EFI_DEVICE_PATH *DevicePath = NULL;
|
||
EFI_DEVICE_PATH *OriginalRootDevicePath = NULL;
|
||
EFI_DEVICE_PATH_ALIGNED DevicePathAligned;
|
||
UINT16 BusNumber = 0;
|
||
UINT8 DeviceNumber = 0;
|
||
UINT8 FunctionNumber = 0;
|
||
BOOLEAN FoundACPIDevice = FALSE;
|
||
BOOLEAN FoundPCIDevice = FALSE;
|
||
EFI_GUID DeviceIoProtocol = DEVICE_IO_PROTOCOL;
|
||
EFI_HANDLE MyHandle;
|
||
EFI_DEVICE_IO_INTERFACE *IoDev;
|
||
|
||
|
||
|
||
|
||
RtlZeroMemory(CardInfo, sizeof(NET_CARD_INFO));
|
||
|
||
|
||
|
||
//
|
||
// Get the device path for the NIC that we're using for PXE.
|
||
//
|
||
FlipToPhysical();
|
||
Status = EfiST->BootServices->HandleProtocol( PXEHandle,
|
||
&EfiDevicePathProtocol,
|
||
&DevicePath );
|
||
FlipToVirtual();
|
||
if( Status != EFI_SUCCESS ) {
|
||
DbgPrint( "NetQueryCardInfo: HandleProtocol(1) failed (%x)\n", Status);
|
||
return (ARC_STATUS)Status;
|
||
}
|
||
|
||
FlipToPhysical();
|
||
EfiAlignDp( &DevicePathAligned,
|
||
DevicePath,
|
||
DevicePathNodeLength(DevicePath));
|
||
|
||
FlipToVirtual();
|
||
|
||
|
||
//
|
||
// Save off this root DevicePath in case we need it later.
|
||
//
|
||
OriginalRootDevicePath = DevicePath;
|
||
|
||
|
||
//
|
||
// Now we need to read the PCI header information from the specific
|
||
// card. To do that, we need to dig out the BusNumber, DeviceNumber and
|
||
// FunctionNumber that help describe this PCI device.
|
||
//
|
||
|
||
//
|
||
// AcpiDevicePath = (ACPI_HID_DEVICE_PATH *)&DevicePathAligned;
|
||
// BusNumber = AcpiDevicePath->UID
|
||
//
|
||
// PciDevicePath = (PCI_DEVICE_PATH *)&DevicePathAligned;
|
||
// DeviceNumber = PciDevicePath->Device
|
||
// FunctionNumber = PciDevicePath->Function
|
||
//
|
||
|
||
FlipToPhysical();
|
||
while( DevicePathAligned.DevPath.Type != END_DEVICE_PATH_TYPE ) {
|
||
|
||
if( (DevicePathAligned.DevPath.Type == ACPI_DEVICE_PATH) &&
|
||
(DevicePathAligned.DevPath.SubType == ACPI_DP) ) {
|
||
|
||
//
|
||
// We'll find the BusNumber here.
|
||
//
|
||
ACPI_HID_DEVICE_PATH *AcpiDevicePath;
|
||
|
||
AcpiDevicePath = (ACPI_HID_DEVICE_PATH *)&DevicePathAligned;
|
||
BusNumber = (UINT16)AcpiDevicePath->UID;
|
||
FoundACPIDevice = TRUE;
|
||
}
|
||
|
||
|
||
if( (DevicePathAligned.DevPath.Type == HARDWARE_DEVICE_PATH) &&
|
||
(DevicePathAligned.DevPath.SubType == HW_PCI_DP) ) {
|
||
|
||
//
|
||
// We'll find the BusNumber here.
|
||
//
|
||
PCI_DEVICE_PATH *PciDevicePath;
|
||
|
||
PciDevicePath = (PCI_DEVICE_PATH *)&DevicePathAligned;
|
||
DeviceNumber = PciDevicePath->Device;
|
||
FunctionNumber = PciDevicePath->Function;
|
||
FoundPCIDevice = TRUE;
|
||
}
|
||
|
||
//
|
||
// Get the next structure in our packed array.
|
||
//
|
||
DevicePath = NextDevicePathNode( DevicePath );
|
||
|
||
EfiAlignDp(&DevicePathAligned,
|
||
DevicePath,
|
||
DevicePathNodeLength(DevicePath));
|
||
|
||
|
||
}
|
||
FlipToVirtual();
|
||
|
||
|
||
|
||
//
|
||
// Derive the function pointer that will allow us to read from
|
||
// PCI space.
|
||
//
|
||
DevicePath = OriginalRootDevicePath;
|
||
FlipToPhysical();
|
||
Status = EfiST->BootServices->LocateDevicePath( &DeviceIoProtocol,
|
||
&DevicePath,
|
||
&MyHandle );
|
||
FlipToVirtual();
|
||
if( Status != EFI_SUCCESS ) {
|
||
DbgPrint( "NetQueryCardInfo: LocateDevicePath failed (%X)\n", Status);
|
||
return (ARC_STATUS)Status;
|
||
}
|
||
|
||
FlipToPhysical();
|
||
Status = EfiST->BootServices->HandleProtocol( MyHandle,
|
||
&DeviceIoProtocol,
|
||
(VOID*)&IoDev );
|
||
FlipToVirtual();
|
||
|
||
if( Status != EFI_SUCCESS ) {
|
||
DbgPrint( "NetQueryCardInfo: HandleProtocol(2) failed (%X)\n", Status);
|
||
return (ARC_STATUS)Status;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// We've got the Bus, Device, and Function number for this device. Go read
|
||
// his header (with the PCI-Read function that we just derived) and get
|
||
// the information we're after.
|
||
//
|
||
if( FoundPCIDevice && FoundACPIDevice ) {
|
||
UINT64 Address;
|
||
PCI_TYPE00 Pci;
|
||
|
||
DbgPrint( "NetQueryCardInfo: Found all the config info for the device.\n" );
|
||
DbgPrint( " BusNumber: %d DeviceNumber: %d FunctionNumber: %d\n", BusNumber, DeviceNumber, FunctionNumber );
|
||
|
||
//
|
||
// Generate the address, then read the PCI header from the device.
|
||
//
|
||
Address = EFI_PCI_ADDRESS( BusNumber, DeviceNumber, FunctionNumber );
|
||
|
||
RtlZeroMemory(&Pci, sizeof(PCI_TYPE00));
|
||
|
||
|
||
FlipToPhysical();
|
||
Status = IoDev->Pci.Read( IoDev,
|
||
IO_UINT32,
|
||
Address,
|
||
sizeof(PCI_TYPE00) / sizeof(UINT32),
|
||
&Pci );
|
||
FlipToVirtual();
|
||
if( Status != EFI_SUCCESS ) {
|
||
DbgPrint( "NetQueryCardInfo: Pci.Read failed (%X)\n", Status);
|
||
return (ARC_STATUS)Status;
|
||
}
|
||
|
||
//
|
||
// It all worked. Copy the information from the device into
|
||
// the CardInfo structure and exit.
|
||
//
|
||
|
||
CardInfo->NicType = 2; // He's PCI
|
||
CardInfo->pci.Vendor_ID = Pci.Hdr.VendorId;
|
||
CardInfo->pci.Dev_ID = Pci.Hdr.DeviceId;
|
||
CardInfo->pci.Rev = Pci.Hdr.RevisionID;
|
||
|
||
// SubSys_ID is actually ((SubsystemID << 16) | SubsystemVendorID)
|
||
CardInfo->pci.Subsys_ID = Pci.Device.SubsystemID;
|
||
CardInfo->pci.Subsys_ID = (CardInfo->pci.Subsys_ID << 16) | (Pci.Device.SubsystemVendorID);
|
||
|
||
#if DBG
|
||
DbgPrint( "\n" );
|
||
DbgPrint( "NetQueryCardInfo: Pci.Hdr.VendorId %x\n", Pci.Hdr.VendorId );
|
||
DbgPrint( " Pci.Hdr.DeviceId %x\n", Pci.Hdr.DeviceId );
|
||
DbgPrint( " Pci.Hdr.Command %x\n", Pci.Hdr.Command );
|
||
DbgPrint( " Pci.Hdr.Status %x\n", Pci.Hdr.Status );
|
||
DbgPrint( " Pci.Hdr.RevisionID %x\n", Pci.Hdr.RevisionID );
|
||
DbgPrint( " Pci.Hdr.HeaderType %x\n", Pci.Hdr.HeaderType );
|
||
DbgPrint( " Pci.Hdr.BIST %x\n", Pci.Hdr.BIST );
|
||
DbgPrint( " Pci.Device.SubsystemVendorID %x\n", Pci.Device.SubsystemVendorID );
|
||
DbgPrint( " Pci.Device.SubsystemID %x\n", Pci.Device.SubsystemID );
|
||
DbgPrint( "\n" );
|
||
|
||
DbgPrint( "NetQueryCardInfo: CardInfo->NicType %x\n", CardInfo->NicType );
|
||
DbgPrint( " CardInfo->pci.Vendor_ID %x\n", CardInfo->pci.Vendor_ID );
|
||
DbgPrint( " CardInfo->pci.Dev_ID %x\n", CardInfo->pci.Dev_ID );
|
||
DbgPrint( " CardInfo->pci.Rev %x\n", CardInfo->pci.Rev );
|
||
DbgPrint( " CardInfo->pci.Subsys_ID %x\n", CardInfo->pci.Subsys_ID );
|
||
DbgPrint( "\n" );
|
||
#endif
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
DbgPrint( "NetQueryCardInfo: Failed to find all the config info for the device.\n" );
|
||
}
|
||
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
|
||
}
|
||
|
||
|
||
#else
|
||
|
||
NTSTATUS
|
||
NetQueryCardInfo(
|
||
IN OUT PNET_CARD_INFO CardInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine queries the ROM for information about the card.
|
||
|
||
Arguments:
|
||
|
||
CardInfo - returns the structure defining the card.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS
|
||
STATUS_UNSUCCESSFUL
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG status;
|
||
t_PXENV_UNDI_GET_NIC_TYPE nicType;
|
||
|
||
RtlZeroMemory(CardInfo, sizeof(NET_CARD_INFO));
|
||
|
||
status = RomGetNicType( &nicType );
|
||
if ((status != PXENV_EXIT_SUCCESS) || (nicType.Status != PXENV_EXIT_SUCCESS)) {
|
||
|
||
#if DBG
|
||
DbgPrint( "RomGetNicType returned 0x%x, nicType.Status = 0x%x. Time to upgrade your netcard ROM\n",
|
||
status, nicType.Status );
|
||
#endif
|
||
status = STATUS_UNSUCCESSFUL;
|
||
|
||
} else {
|
||
|
||
#if DBG
|
||
if ( nicType.NicType == 2 ) {
|
||
DbgPrint( "Vendor_ID: %04x, Dev_ID: %04x\n",
|
||
nicType.pci_pnp_info.pci.Vendor_ID,
|
||
nicType.pci_pnp_info.pci.Dev_ID );
|
||
DbgPrint( "Base_Class: %02x, Sub_Class: %02x, Prog_Intf: %02x\n",
|
||
nicType.pci_pnp_info.pci.Base_Class,
|
||
nicType.pci_pnp_info.pci.Sub_Class,
|
||
nicType.pci_pnp_info.pci.Prog_Intf );
|
||
DbgPrint( "Rev: %02x, BusDevFunc: %04x, SubSystem: %04x\n",
|
||
nicType.pci_pnp_info.pci.Rev,
|
||
nicType.pci_pnp_info.pci.BusDevFunc,
|
||
nicType.pci_pnp_info.pci.Subsys_ID );
|
||
} else {
|
||
DbgPrint( "NicType: 0x%x EISA_Dev_ID: %08x\n",
|
||
nicType.NicType,
|
||
nicType.pci_pnp_info.pnp.EISA_Dev_ID );
|
||
DbgPrint( "Base_Class: %02x, Sub_Class: %02x, Prog_Intf: %02x\n",
|
||
nicType.pci_pnp_info.pnp.Base_Class,
|
||
nicType.pci_pnp_info.pnp.Sub_Class,
|
||
nicType.pci_pnp_info.pnp.Prog_Intf );
|
||
DbgPrint( "CardSelNum: %04x\n",
|
||
nicType.pci_pnp_info.pnp.CardSelNum );
|
||
}
|
||
#endif
|
||
//
|
||
// The call worked, so copy the information.
|
||
//
|
||
|
||
CardInfo->NicType = nicType.NicType;
|
||
if (nicType.NicType == 2) {
|
||
|
||
CardInfo->pci.Vendor_ID = nicType.pci_pnp_info.pci.Vendor_ID;
|
||
CardInfo->pci.Dev_ID = nicType.pci_pnp_info.pci.Dev_ID;
|
||
CardInfo->pci.Base_Class = nicType.pci_pnp_info.pci.Base_Class;
|
||
CardInfo->pci.Sub_Class = nicType.pci_pnp_info.pci.Sub_Class;
|
||
CardInfo->pci.Prog_Intf = nicType.pci_pnp_info.pci.Prog_Intf;
|
||
CardInfo->pci.Rev = nicType.pci_pnp_info.pci.Rev;
|
||
CardInfo->pci.BusDevFunc = nicType.pci_pnp_info.pci.BusDevFunc;
|
||
CardInfo->pci.Subsys_ID = nicType.pci_pnp_info.pci.Subsys_ID;
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
#endif // EFI
|
||
|
||
NTSTATUS
|
||
UdpSendAndReceiveForNetQuery(
|
||
IN PVOID SendBuffer,
|
||
IN ULONG SendBufferLength,
|
||
IN ULONG SendRemoteHost,
|
||
IN USHORT SendRemotePort,
|
||
IN ULONG SendRetryCount,
|
||
IN PVOID ReceiveBuffer,
|
||
IN ULONG ReceiveBufferLength,
|
||
IN ULONG ReceiveTimeout,
|
||
IN ULONG ReceiveSignatureCount,
|
||
IN PCHAR ReceiveSignatures[]
|
||
)
|
||
{
|
||
ULONG i, j;
|
||
ULONG length;
|
||
ULONG RemoteHost;
|
||
USHORT RemotePort;
|
||
|
||
//
|
||
// Try sending the packet SendRetryCount times, until we receive
|
||
// a response with the right signature, waiting ReceiveTimeout
|
||
// each time.
|
||
//
|
||
|
||
for (i = 0; i < SendRetryCount; i++) {
|
||
|
||
length = UdpSend(
|
||
SendBuffer,
|
||
SendBufferLength,
|
||
SendRemoteHost,
|
||
SendRemotePort);
|
||
|
||
if ( length != SendBufferLength ) {
|
||
DbgPrint("UdpSend only sent %d bytes, not %d\n", length, SendBufferLength);
|
||
return STATUS_UNEXPECTED_NETWORK_ERROR;
|
||
}
|
||
|
||
ReReceive:
|
||
|
||
//
|
||
// NULL out the first 12 bytes in case we get shorter data.
|
||
//
|
||
|
||
memset(ReceiveBuffer, 0x0, 12);
|
||
|
||
length = UdpReceive(
|
||
ReceiveBuffer,
|
||
ReceiveBufferLength,
|
||
&RemoteHost,
|
||
&RemotePort,
|
||
ReceiveTimeout);
|
||
|
||
if ( length == 0 ) {
|
||
DPRINT( ERROR, ("UdpReceive timed out\n") );
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Make sure the signature is one of the ones we expect.
|
||
//
|
||
|
||
for (j = 0; j < ReceiveSignatureCount; j++) {
|
||
if (memcmp(ReceiveBuffer, ReceiveSignatures[j], 4) == 0) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
DbgPrint("UdpReceive got wrong signature\n");
|
||
|
||
// ISSUE NTRAID #60513: CLEAN THIS UP -- but the idea is not to UdpSend
|
||
// again just because we got a bad signature. Still need to respect the
|
||
// original ReceiveTimeout however!
|
||
|
||
goto ReReceive;
|
||
|
||
}
|
||
|
||
//
|
||
// We timed out.
|
||
//
|
||
|
||
return STATUS_IO_TIMEOUT;
|
||
}
|
||
|
||
#define NETCARD_REQUEST_RESPONSE_BUFFER_SIZE 4096
|
||
UCHAR NetCardResponseBuffer[NETCARD_REQUEST_RESPONSE_BUFFER_SIZE];
|
||
|
||
NTSTATUS
|
||
NetQueryDriverInfo(
|
||
IN PNET_CARD_INFO CardInfo,
|
||
IN PCHAR SetupPath,
|
||
IN PCHAR NtBootPathName,
|
||
IN OUT PWCHAR HardwareId,
|
||
IN ULONG HardwareIdLength,
|
||
IN OUT PWCHAR DriverName,
|
||
IN OUT PCHAR DriverNameAnsi OPTIONAL,
|
||
IN ULONG DriverNameLength,
|
||
IN OUT PWCHAR ServiceName,
|
||
IN ULONG ServiceNameLength,
|
||
OUT PCHAR * Registry,
|
||
OUT ULONG * RegistryLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does an exchange with the server to get information
|
||
about the card described by CardInfo.
|
||
|
||
Arguments:
|
||
|
||
CardInfo - Information about the card.
|
||
|
||
SetupPath - UNC path (with only a single leading backslash) to our setup directory
|
||
|
||
NtBootPathName - UNC path (with only a single leading backslash) to our boot directory
|
||
|
||
HardwareId - returns the hardware ID of the card.
|
||
|
||
HardwareIdLength - the length (in bytes) of the passed-in HardwareId buffer.
|
||
|
||
DriverName - returns the name of the driver.
|
||
|
||
DriverNameAnsi - if present, returns the name of the driver in ANSI.
|
||
|
||
DriverNameLength - the length (in bytes) of the passed-in DriverName buffer
|
||
(it is assumed that DriverNameAnsi is at least half this length).
|
||
|
||
ServiceName - returns the service key of the driver.
|
||
|
||
ServiceNameLength - the length (in bytes) of the passed-in ServiceName buffer.
|
||
|
||
Registry - if needed, allocates and returns extra registry parameters
|
||
for the card.
|
||
|
||
RegistryLength - the length of Registry.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS
|
||
STATUS_BUFFER_OVERFLOW if either of the buffers are too small.
|
||
STATUS_INSUFFICIENT_RESOURCES if we cannot allocate memory for Registry.
|
||
STATUS_IO_TIMEOUT if we can't get a response from the server.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
USHORT localPort;
|
||
PNETCARD_REQUEST_PACKET requestPacket;
|
||
PCHAR ReceiveSignatures[2];
|
||
PCHAR ReceiveBuffer;
|
||
ULONG GuidLength;
|
||
PUCHAR Guid;
|
||
ULONG sendSize;
|
||
PNETCARD_REQUEST_PACKET allocatedRequestPacket = NULL;
|
||
|
||
|
||
//
|
||
// Get the local UDP port.
|
||
//
|
||
|
||
localPort = UdpUnicastDestinationPort;
|
||
|
||
//
|
||
// Now construct the outgoing packet.
|
||
//
|
||
|
||
|
||
//
|
||
// Don't allocate ReceiveBuffer. We're about to call UdpSend, and he's
|
||
// going to want a physical address on ia64. If we just use a static
|
||
// here, the virtual-physical mapping will be 1-to-1. This isn't a big
|
||
// deal since the allocation is hardcoded to NETCARD_REQUEST_RESPONSE_BUFFER_SIZE
|
||
// and is never freed anyway.
|
||
//
|
||
// ReceiveBuffer = BlAllocateHeap( NETCARD_REQUEST_RESPONSE_BUFFER_SIZE );
|
||
//
|
||
ReceiveBuffer = NetCardResponseBuffer;
|
||
|
||
if (ReceiveBuffer == NULL) {
|
||
|
||
return STATUS_BUFFER_OVERFLOW;
|
||
}
|
||
|
||
requestPacket = (PNETCARD_REQUEST_PACKET) ReceiveBuffer;
|
||
|
||
RtlCopyMemory(requestPacket->Signature, NetcardRequestSignature, sizeof(requestPacket->Signature));
|
||
requestPacket->Length = sizeof(NETCARD_REQUEST_PACKET) - FIELD_OFFSET(NETCARD_REQUEST_PACKET, Version);
|
||
requestPacket->Version = OSCPKT_NETCARD_REQUEST_VERSION;
|
||
|
||
#if defined(_ALPHA_)
|
||
#if defined(_AXP64)
|
||
requestPacket->Architecture = PROCESSOR_ARCHITECTURE_ALPHA64;
|
||
#else
|
||
requestPacket->Architecture = PROCESSOR_ARCHITECTURE_ALPHA;
|
||
#endif
|
||
#endif
|
||
#if defined(_MIPS_)
|
||
requestPacket->Architecture = PROCESSOR_ARCHITECTURE_MIPS;
|
||
#endif
|
||
#if defined(_PPC_)
|
||
requestPacket->Architecture = PROCESSOR_ARCHITECTURE_PPC;
|
||
#endif
|
||
#if defined(_IA64_)
|
||
requestPacket->Architecture = PROCESSOR_ARCHITECTURE_IA64;
|
||
#endif
|
||
#if defined(_X86_)
|
||
requestPacket->Architecture = PROCESSOR_ARCHITECTURE_INTEL;
|
||
#endif
|
||
|
||
requestPacket->SetupDirectoryLength = SetupPath ? (strlen( SetupPath ) + 1) : 0;
|
||
|
||
#if defined(REMOTE_BOOT)
|
||
if (NtBootPathName != NULL) {
|
||
|
||
ULONG bufferOffset;
|
||
|
||
requestPacket->FileCheckAndCopy = (ULONG)TRUE;
|
||
|
||
//
|
||
// make it a fully qualified UNC by sticking on a leading slash and
|
||
// appending the "\system32\drivers" to form the full drivers
|
||
// directory path.
|
||
//
|
||
|
||
requestPacket->DriverDirectoryLength = sizeof( "\\\\System32\\Drivers" );
|
||
requestPacket->DriverDirectoryLength += strlen( NtBootPathName );
|
||
|
||
sendSize = sizeof(NETCARD_REQUEST_PACKET) +
|
||
requestPacket->DriverDirectoryLength +
|
||
requestPacket->SetupDirectoryLength;
|
||
|
||
requestPacket->DriverDirectoryPath[0] = '\\';
|
||
strcpy( &requestPacket->DriverDirectoryPath[1], NtBootPathName );
|
||
strcat( requestPacket->DriverDirectoryPath, "\\SYSTEM32\\DRIVERS" );
|
||
|
||
bufferOffset = strlen( requestPacket->DriverDirectoryPath ) + 1;
|
||
|
||
if (requestPacket->SetupDirectoryLength) {
|
||
|
||
requestPacket->DriverDirectoryPath[bufferOffset++] = '\\';
|
||
strcpy( &requestPacket->DriverDirectoryPath[bufferOffset], SetupPath );
|
||
}
|
||
|
||
} else {
|
||
|
||
requestPacket->FileCheckAndCopy = (ULONG)FALSE;
|
||
requestPacket->DriverDirectoryLength = 0;
|
||
#endif
|
||
|
||
sendSize = sizeof(NETCARD_REQUEST_PACKET) + requestPacket->SetupDirectoryLength;
|
||
|
||
if (requestPacket->SetupDirectoryLength) {
|
||
|
||
requestPacket->SetupDirectoryPath[0] = '\\';
|
||
strcpy( &requestPacket->SetupDirectoryPath[1], SetupPath );
|
||
}
|
||
|
||
#if defined(REMOTE_BOOT)
|
||
}
|
||
#endif
|
||
|
||
GetGuid(&Guid, &GuidLength);
|
||
|
||
if (GuidLength == sizeof(requestPacket->Guid)) {
|
||
memcpy(requestPacket->Guid, Guid, GuidLength);
|
||
}
|
||
RtlCopyMemory(&requestPacket->CardInfo, CardInfo, sizeof(NET_CARD_INFO));
|
||
|
||
ReceiveSignatures[0] = NetcardResponseSignature;
|
||
ReceiveSignatures[1] = NetcardErrorSignature;
|
||
|
||
Status = UdpSendAndReceiveForNetQuery(
|
||
requestPacket,
|
||
sendSize,
|
||
NetServerIpAddress,
|
||
BINL_PORT,
|
||
4, // retry count
|
||
ReceiveBuffer,
|
||
NETCARD_REQUEST_RESPONSE_BUFFER_SIZE,
|
||
60, // receive timeout... may have to parse INF files
|
||
2,
|
||
ReceiveSignatures
|
||
);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
|
||
PWCHAR stringInPacket;
|
||
ULONG maxOffset;
|
||
UNICODE_STRING uString;
|
||
ULONG len;
|
||
PNETCARD_RESPONSE_PACKET responsePacket;
|
||
|
||
responsePacket = (PNETCARD_RESPONSE_PACKET)ReceiveBuffer;
|
||
|
||
if (responsePacket->Status != STATUS_SUCCESS) {
|
||
return responsePacket->Status;
|
||
}
|
||
|
||
if (responsePacket->Length < sizeof( NETCARD_RESPONSE_PACKET )) {
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// The exchange succeeded, so copy the results back.
|
||
//
|
||
|
||
maxOffset = NETCARD_REQUEST_RESPONSE_BUFFER_SIZE -
|
||
sizeof( NETCARD_RESPONSE_PACKET );
|
||
|
||
if (responsePacket->HardwareIdOffset < sizeof(NETCARD_RESPONSE_PACKET) ||
|
||
responsePacket->HardwareIdOffset >= maxOffset ) {
|
||
|
||
return STATUS_BUFFER_OVERFLOW;
|
||
}
|
||
|
||
//
|
||
// pick up the hardwareId string. It's given to us as an offset
|
||
// within the packet to a unicode null terminated string.
|
||
//
|
||
|
||
stringInPacket = (PWCHAR)(PCHAR)((PCHAR)responsePacket +
|
||
responsePacket->HardwareIdOffset );
|
||
|
||
RtlInitUnicodeString( &uString, stringInPacket );
|
||
|
||
if (uString.Length + sizeof(WCHAR) > HardwareIdLength) {
|
||
return STATUS_BUFFER_OVERFLOW;
|
||
}
|
||
|
||
RtlCopyMemory( HardwareId, uString.Buffer, uString.Length + sizeof(WCHAR));
|
||
|
||
//
|
||
// pick up the driverName string. It's given to us as an offset
|
||
// within the packet to a unicode null terminated string.
|
||
//
|
||
|
||
stringInPacket = (PWCHAR)(PCHAR)((PCHAR)responsePacket +
|
||
responsePacket->DriverNameOffset );
|
||
|
||
RtlInitUnicodeString( &uString, stringInPacket );
|
||
|
||
if (uString.Length + sizeof(WCHAR) > DriverNameLength) {
|
||
return STATUS_BUFFER_OVERFLOW;
|
||
}
|
||
|
||
RtlCopyMemory( DriverName, uString.Buffer, uString.Length + sizeof(WCHAR));
|
||
|
||
//
|
||
// we convert this one into ansi if the caller requested
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(DriverNameAnsi)) {
|
||
|
||
RtlUnicodeToMultiByteN( DriverNameAnsi,
|
||
DriverNameLength,
|
||
NULL,
|
||
uString.Buffer,
|
||
uString.Length + sizeof(WCHAR));
|
||
}
|
||
|
||
//
|
||
// pick up the serviceName string. It's given to us as an offset
|
||
// within the packet to a unicode null terminated string.
|
||
//
|
||
|
||
stringInPacket = (PWCHAR)(PCHAR)((PCHAR)responsePacket +
|
||
responsePacket->ServiceNameOffset );
|
||
|
||
RtlInitUnicodeString( &uString, stringInPacket );
|
||
|
||
if (uString.Length + sizeof(WCHAR) > ServiceNameLength) {
|
||
return STATUS_BUFFER_OVERFLOW;
|
||
}
|
||
|
||
RtlCopyMemory( ServiceName, uString.Buffer, uString.Length + sizeof(WCHAR));
|
||
|
||
//
|
||
// If any extra registry params were passed back, allocate/copy those.
|
||
//
|
||
|
||
*RegistryLength = responsePacket->RegistryLength;
|
||
|
||
if (*RegistryLength) {
|
||
|
||
*Registry = BlAllocateHeap(*RegistryLength);
|
||
if (*Registry == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
stringInPacket = (PWCHAR)(PCHAR)((PCHAR)responsePacket +
|
||
responsePacket->RegistryOffset );
|
||
|
||
RtlCopyMemory(*Registry, stringInPacket, *RegistryLength);
|
||
|
||
} else {
|
||
|
||
*Registry = NULL;
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
#if defined(REMOTE_BOOT)
|
||
NTSTATUS
|
||
UdpSendAndReceiveForIpsec(
|
||
IN PVOID SendBuffer,
|
||
IN ULONG SendBufferLength,
|
||
IN ULONG SendRemoteHost,
|
||
IN USHORT SendRemotePort,
|
||
IN PVOID ReceiveBuffer,
|
||
IN ULONG ReceiveBufferLength,
|
||
IN ULONG ReceiveTimeout
|
||
)
|
||
{
|
||
ULONG i, j;
|
||
ULONG length;
|
||
ULONG RemoteHost;
|
||
USHORT RemotePort;
|
||
|
||
length = UdpSend(
|
||
SendBuffer,
|
||
SendBufferLength,
|
||
SendRemoteHost,
|
||
SendRemotePort);
|
||
|
||
if ( length != SendBufferLength ) {
|
||
DbgPrint("UdpSend only sent %d bytes, not %d\n", length, SendBufferLength);
|
||
return STATUS_UNEXPECTED_NETWORK_ERROR;
|
||
}
|
||
|
||
length = UdpReceive(
|
||
ReceiveBuffer,
|
||
ReceiveBufferLength,
|
||
&RemoteHost,
|
||
&RemotePort,
|
||
ReceiveTimeout);
|
||
|
||
if ( length == 0 ) {
|
||
DPRINT( ERROR, ("UdpReceive timed out\n") );
|
||
return STATUS_IO_TIMEOUT;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
ULONG
|
||
ConnItoa (
|
||
IN ULONG Value,
|
||
OUT PUCHAR Buffer
|
||
);
|
||
|
||
ULONG
|
||
ConnSafeAtol (
|
||
IN PUCHAR Buffer,
|
||
IN PUCHAR BufferEnd
|
||
);
|
||
|
||
NTSTATUS
|
||
NetPrepareIpsec(
|
||
IN ULONG InboundSpi,
|
||
OUT ULONG * SessionKey,
|
||
OUT ULONG * OutboundSpi
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does an exchange with the server to set up for a future
|
||
IPSEC conversation. We pass him the SPI we will use for our inbound
|
||
conversation (from the server to us). He replies with the the session
|
||
key that he has generated and the outbound SPI he has gotten (for the
|
||
conversation from us to him). We also return the IP address of the
|
||
server in this call.
|
||
|
||
Arguments:
|
||
|
||
InboundSpi - The SPI for the conversation from the server to us. This
|
||
is typically generated by the caller, and will later be passed to
|
||
IPSEC in an IOCTL_IPSEC_SET_SPI.
|
||
|
||
SessionKey - Returns the server-generated session key.
|
||
|
||
OutboundSpi - The server will take the information we give him and
|
||
call IOCTL_IPSEC_GET_SPI, this returns the SPI he is assigned for
|
||
the conversation from us to the server.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS
|
||
STATUS_BUFFER_OVERFLOW if either of the buffers are too small.
|
||
STATUS_INSUFFICIENT_RESOURCES if we cannot allocate memory for Registry.
|
||
STATUS_IO_TIMEOUT if we can't get a response from the server.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
USHORT localPort;
|
||
CHAR SendBuffer[128];
|
||
PCHAR SendBufferLoc;
|
||
PCHAR ReceiveSignatures[2];
|
||
CHAR ReceiveBuffer[512];
|
||
CHAR SignBuffer[NTLMSSP_MESSAGE_SIGNATURE_SIZE];
|
||
CHAR KeyBuffer[4];
|
||
ULONG SendCount = 0;
|
||
ULONG SecurityHandle;
|
||
ULONG Nibble;
|
||
PUCHAR CurLoc, Options;
|
||
BOOLEAN SpiReceived, SecurityReceived, SignReceived, KeyReceived;
|
||
SecBufferDesc SignMessage;
|
||
SecBuffer SigBuffers[2];
|
||
SECURITY_STATUS SecStatus;
|
||
|
||
//
|
||
// Get the local UDP port.
|
||
//
|
||
|
||
localPort = UdpUnicastDestinationPort;
|
||
|
||
//
|
||
// Loop until we get a valid response.
|
||
//
|
||
|
||
while (SendCount < 5) {
|
||
|
||
//
|
||
// Construct the outgoing TFTP packet. We increment the
|
||
// sequence number by one each time. We have to make sure
|
||
// that the response we receive matches our last sequence
|
||
// numbet sent since the server may generate a new key each
|
||
// time.
|
||
//
|
||
|
||
SendBuffer[0] = 0;
|
||
SendBuffer[1] = 17;
|
||
SendBuffer[2] = 0x00;
|
||
SendBuffer[3] = (UCHAR)(SendCount + 1);
|
||
SendBufferLoc = SendBuffer+4;
|
||
strcpy(SendBufferLoc, "spi");
|
||
SendBufferLoc += sizeof("spi");
|
||
SendBufferLoc += ConnItoa(InboundSpi, SendBufferLoc);
|
||
#if defined(REMOTE_BOOT_SECURITY)
|
||
strcpy(SendBufferLoc, "security");
|
||
SendBufferLoc += sizeof("security");
|
||
SendBufferLoc += ConnItoa(TftpSecurityHandle, SendBufferLoc);
|
||
#endif // defined(REMOTE_BOOT_SECURITY)
|
||
|
||
memset(ReceiveBuffer, 0x0, sizeof(ReceiveBuffer));
|
||
|
||
Status = UdpSendAndReceiveForIpsec(
|
||
SendBuffer,
|
||
(ULONG)(SendBufferLoc - SendBuffer),
|
||
NetServerIpAddress,
|
||
TFTP_PORT,
|
||
ReceiveBuffer,
|
||
sizeof(ReceiveBuffer),
|
||
3 // receive timeout
|
||
);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
|
||
if ((ReceiveBuffer[1] == 17) &&
|
||
(ReceiveBuffer[3] == (UCHAR)(SendCount+1))) {
|
||
|
||
Options = ReceiveBuffer+4;
|
||
|
||
SpiReceived = FALSE;
|
||
SecurityReceived = FALSE;
|
||
SignReceived = FALSE;
|
||
KeyReceived = FALSE;
|
||
|
||
while (*Options != '\0') {
|
||
|
||
if (strcmp(Options, "spi") == 0) {
|
||
|
||
Options += sizeof("spi");
|
||
|
||
*OutboundSpi = ConnSafeAtol(Options, ReceiveBuffer+sizeof(ReceiveBuffer));
|
||
if (*OutboundSpi != (ULONG)-1) {
|
||
SpiReceived = TRUE;
|
||
}
|
||
Options += strlen(Options) + 1;
|
||
|
||
} else if (strcmp(Options, "security") == 0) {
|
||
|
||
Options += sizeof("security");
|
||
|
||
SecurityHandle = ConnSafeAtol(Options, ReceiveBuffer + sizeof(ReceiveBuffer));
|
||
if (SecurityHandle != (ULONG)-1) {
|
||
SecurityReceived = TRUE;
|
||
}
|
||
Options += strlen(Options) + 1;
|
||
|
||
} else if (strcmp(Options, "sign") == 0) {
|
||
|
||
Options += sizeof("sign");
|
||
Options += ConnAtosign(Options, sizeof(SignBuffer), SignBuffer);
|
||
SignReceived = TRUE;
|
||
|
||
} else if (strcmp(Options, "key") == 0) {
|
||
|
||
Options += sizeof("key");
|
||
Options += ConnAtosign(Options, sizeof(KeyBuffer), KeyBuffer);
|
||
KeyReceived = TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Unknown option.
|
||
//
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
#if defined(REMOTE_BOOT_SECURITY)
|
||
|
||
if (SpiReceived && SecurityReceived && SignReceived && KeyReceived) {
|
||
|
||
if (SecurityHandle != TftpSecurityHandle) {
|
||
|
||
DbgPrint("Got incorrect security handle in response\n");
|
||
Status = STATUS_INVALID_HANDLE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Decrypt the key using the sign.
|
||
//
|
||
|
||
SigBuffers[1].pvBuffer = SignBuffer;
|
||
SigBuffers[1].cbBuffer = NTLMSSP_MESSAGE_SIGNATURE_SIZE;
|
||
SigBuffers[1].BufferType = SECBUFFER_TOKEN;
|
||
|
||
SigBuffers[0].pvBuffer = KeyBuffer;
|
||
SigBuffers[0].cbBuffer = sizeof(KeyBuffer);
|
||
SigBuffers[0].BufferType = SECBUFFER_DATA;
|
||
|
||
SignMessage.pBuffers = SigBuffers;
|
||
SignMessage.cBuffers = 2;
|
||
SignMessage.ulVersion = 0;
|
||
|
||
ASSERT (TftpClientContextHandleValid);
|
||
|
||
SecStatus = UnsealMessage(
|
||
&TftpClientContextHandle,
|
||
&SignMessage,
|
||
0,
|
||
0 );
|
||
|
||
if ( SecStatus != SEC_E_OK ) {
|
||
|
||
DbgPrint("NetPrepareIpsec: UnsealMessage failed %x\n", SecStatus);
|
||
Status = STATUS_UNEXPECTED_NETWORK_ERROR;
|
||
|
||
} else {
|
||
|
||
*SessionKey = *(PULONG)KeyBuffer;
|
||
|
||
DbgPrint("NetPrepareIpsec: Send SPI %d, got key %d (%lx) and response %d\n",
|
||
InboundSpi, *SessionKey, *SessionKey, *OutboundSpi);
|
||
|
||
Status = STATUS_SUCCESS;
|
||
break; // exit (SendCount < 5) loop
|
||
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
DbgPrint("Response had no SPI, security, sign, or key!\n");
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
|
||
}
|
||
|
||
#else // defined(REMOTE_BOOT_SECURITY)
|
||
|
||
if (SpiReceived && KeyReceived) {
|
||
|
||
// if (SendCount == 0) { DbgPrint("SKIPPING!!\n"); ++SendCount; continue; } // drop a frame to test retry
|
||
|
||
*SessionKey = *(PULONG)KeyBuffer;
|
||
|
||
DbgPrint("NetPrepareIpsec: Send SPI %d, got key %d (%lx) and response %d\n",
|
||
InboundSpi, *SessionKey, *SessionKey, *OutboundSpi);
|
||
|
||
Status = STATUS_SUCCESS;
|
||
break; // exit (SendCount < 5) loop
|
||
|
||
} else {
|
||
|
||
DbgPrint("Response had no SPI or key!\n");
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
|
||
}
|
||
|
||
#endif // defined(REMOTE_BOOT_SECURITY)
|
||
|
||
} else {
|
||
|
||
DbgPrint("Got bogus response from IPSEC request!!\n");
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
}
|
||
|
||
++SendCount;
|
||
|
||
}
|
||
|
||
return Status;
|
||
|
||
}
|
||
#endif // defined(REMOTE_BOOT)
|
||
|
||
|
||
#if defined(REMOTE_BOOT)
|
||
NTSTATUS
|
||
NetCopyHalAndKernel(
|
||
IN PCHAR HalName,
|
||
IN PCHAR Guid,
|
||
IN ULONG GuidLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes a detected HAL name and this machine's GUID, passing them
|
||
to the BINL server
|
||
|
||
Arguments:
|
||
|
||
HalName - The detected Hal.
|
||
|
||
Guid - The Guid for this machine.
|
||
|
||
GuidLength - Number of bytes in Guid.
|
||
|
||
Return Value:
|
||
|
||
Status - STATUS_SUCCESS if all goes well.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
USHORT localPort;
|
||
HAL_REQUEST_PACKET requestPacket;
|
||
HAL_RESPONSE_PACKET responsePacket;
|
||
PCHAR ReceiveSignatures[1];
|
||
|
||
//
|
||
// Get the local UDP port.
|
||
//
|
||
|
||
localPort = UdpUnicastDestinationPort;
|
||
|
||
//
|
||
// Now construct the outgoing packet.
|
||
//
|
||
|
||
RtlCopyMemory(requestPacket.Signature, HalRequestSignature, sizeof(requestPacket.Signature));
|
||
requestPacket.Length = sizeof(HAL_REQUEST_PACKET) - FIELD_OFFSET(HAL_REQUEST_PACKET, Guid);
|
||
strcpy(requestPacket.HalName, HalName);
|
||
memset(requestPacket.Guid, 0x0, sizeof(requestPacket.Guid));
|
||
memcpy(requestPacket.Guid, Guid, GuidLength);
|
||
requestPacket.GuidLength = GuidLength;
|
||
|
||
ReceiveSignatures[0] = HalResponseSignature;
|
||
|
||
Status = UdpSendAndReceiveForNetQuery(
|
||
&requestPacket,
|
||
sizeof(HAL_REQUEST_PACKET),
|
||
NetServerIpAddress,
|
||
BINL_PORT,
|
||
4, // retry count
|
||
&responsePacket,
|
||
sizeof(responsePacket),
|
||
15, // receive timeout
|
||
2,
|
||
ReceiveSignatures
|
||
);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
Status = responsePacket.Status;
|
||
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
#endif // defined(REMOTE_BOOT)
|
||
|