/** @file
  Functions declaration related with DHCPv4 for UefiPxeBc Driver.

  Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#pragma once

#define PXEBC_DHCP4_OPTION_MAX_NUM    16
#define PXEBC_DHCP4_OPTION_MAX_SIZE   312
#define PXEBC_DHCP4_PACKET_MAX_SIZE   (sizeof (EFI_PXE_BASE_CODE_PACKET))
#define PXEBC_DHCP4_S_PORT            67
#define PXEBC_DHCP4_C_PORT            68
#define PXEBC_BS_DOWNLOAD_PORT        69
#define PXEBC_BS_DISCOVER_PORT        4011
#define PXEBC_DHCP4_OPCODE_REQUEST    1
#define PXEBC_DHCP4_OPCODE_REPLY      2
#define PXEBC_DHCP4_MSG_TYPE_REQUEST  3
#define PXEBC_DHCP4_MAGIC             0x63538263      // network byte order

//
// Sub-Options in Dhcp Vendor Option
//
#define PXEBC_VENDOR_TAG_MTFTP_IP          1
#define PXEBC_VENDOR_TAG_MTFTP_CPORT       2
#define PXEBC_VENDOR_TAG_MTFTP_SPORT       3
#define PXEBC_VENDOR_TAG_MTFTP_TIMEOUT     4
#define PXEBC_VENDOR_TAG_MTFTP_DELAY       5
#define PXEBC_VENDOR_TAG_DISCOVER_CTRL     6
#define PXEBC_VENDOR_TAG_DISCOVER_MCAST    7
#define PXEBC_VENDOR_TAG_BOOT_SERVERS      8
#define PXEBC_VENDOR_TAG_BOOT_MENU         9
#define PXEBC_VENDOR_TAG_MENU_PROMPT       10
#define PXEBC_VENDOR_TAG_MCAST_ALLOC       11
#define PXEBC_VENDOR_TAG_CREDENTIAL_TYPES  12
#define PXEBC_VENDOR_TAG_BOOT_ITEM         71

#define PXEBC_BOOT_REQUEST_TIMEOUT  1
#define PXEBC_BOOT_REQUEST_RETRIES  4

#define PXEBC_DHCP4_OVERLOAD_FILE         1
#define PXEBC_DHCP4_OVERLOAD_SERVER_NAME  2

//
// The array index of the DHCP4 option tag interested
//
#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN  0
#define PXEBC_DHCP4_TAG_INDEX_VENDOR        1
#define PXEBC_DHCP4_TAG_INDEX_OVERLOAD      2
#define PXEBC_DHCP4_TAG_INDEX_MSG_TYPE      3
#define PXEBC_DHCP4_TAG_INDEX_SERVER_ID     4
#define PXEBC_DHCP4_TAG_INDEX_CLASS_ID      5
#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE      6
#define PXEBC_DHCP4_TAG_INDEX_MAX           7

//
// Dhcp4 and Dhcp6 share this definition, and corresponding
// relationship is as follows:
//
//   Dhcp4Discover <> Dhcp6Solicit
//   Dhcp4Offer    <> Dhcp6Advertise
//   Dhcp4Request  <> Dhcp6Request
//   Dhcp4Ack      <> DHcp6Reply
//
typedef enum {
  PxeOfferTypeDhcpOnly,
  PxeOfferTypeDhcpPxe10,
  PxeOfferTypeDhcpWfm11a,
  PxeOfferTypeDhcpBinl,
  PxeOfferTypeProxyPxe10,
  PxeOfferTypeProxyWfm11a,
  PxeOfferTypeProxyBinl,
  PxeOfferTypeBootp,
  PxeOfferTypeMax
} PXEBC_OFFER_TYPE;

#define BIT(x)   (1 << x)
#define CTRL(x)  (0x1F & (x))
#define DEFAULT_CLASS_ID_DATA  "PXEClient:Arch:xxxxx:UNDI:003000"
#define DEFAULT_UNDI_TYPE      1
#define DEFAULT_UNDI_MAJOR     3
#define DEFAULT_UNDI_MINOR     0

#define MTFTP_VENDOR_OPTION_BIT_MAP \
  (BIT (PXEBC_VENDOR_TAG_MTFTP_IP) | \
   BIT (PXEBC_VENDOR_TAG_MTFTP_CPORT) | \
   BIT (PXEBC_VENDOR_TAG_MTFTP_SPORT) | \
   BIT (PXEBC_VENDOR_TAG_MTFTP_TIMEOUT) | \
   BIT (PXEBC_VENDOR_TAG_MTFTP_DELAY))

#define DISCOVER_VENDOR_OPTION_BIT_MAP \
  (BIT (PXEBC_VENDOR_TAG_DISCOVER_CTRL) | \
   BIT (PXEBC_VENDOR_TAG_DISCOVER_MCAST) | \
   BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS) | \
   BIT (PXEBC_VENDOR_TAG_BOOT_MENU) | \
   BIT (PXEBC_VENDOR_TAG_MENU_PROMPT))

#define IS_VALID_BOOT_SERVERS(x) \
  ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS)) \
   == BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS))

#define IS_VALID_BOOT_PROMPT(x) \
  ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_MENU_PROMPT)) \
   == BIT (PXEBC_VENDOR_TAG_MENU_PROMPT))

#define IS_VALID_BOOT_MENU(x) \
  ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_BOOT_MENU)) \
   == BIT (PXEBC_VENDOR_TAG_BOOT_MENU))

#define IS_VALID_MTFTP_VENDOR_OPTION(x) \
  (((UINT32) ((x)[0]) & MTFTP_VENDOR_OPTION_BIT_MAP) \
   == MTFTP_VENDOR_OPTION_BIT_MAP)

#define IS_VALID_DISCOVER_VENDOR_OPTION(x) \
  (((UINT32) ((x)[0]) & DISCOVER_VENDOR_OPTION_BIT_MAP) != 0)

#define IS_VALID_CREDENTIAL_VENDOR_OPTION(x) \
  (((UINT32) ((x)[0]) & BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES)) \
   == BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES))

#define IS_VALID_BOOTITEM_VENDOR_OPTION(x) \
  (((UINT32) ((x)[PXEBC_VENDOR_TAG_BOOT_ITEM / 32]) & \
     BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32)) \
    == BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32))

#define SET_VENDOR_OPTION_BIT_MAP(x, y) \
  (*(x + ((y) / 32)) = (UINT32) ((UINT32) ((x)[(y) / 32]) | BIT ((y) % 32)))

#define GET_NEXT_DHCP_OPTION(Opt) \
  (EFI_DHCP4_PACKET_OPTION *) ((UINT8 *) (Opt) + \
   sizeof (EFI_DHCP4_PACKET_OPTION) + (Opt)->Length - 1)

#define GET_OPTION_BUFFER_LEN(Pkt) \
  ((Pkt)->Length - sizeof (EFI_DHCP4_HEADER) - 4)

#define GET_NEXT_BOOT_SVR_ENTRY(Ent) \
  (PXEBC_BOOT_SVR_ENTRY *) ((UINT8 *) Ent + sizeof (*(Ent)) + \
   ((Ent)->IpCnt - 1) * sizeof (EFI_IPv4_ADDRESS))

#define IS_PROXY_DHCP_OFFER(Offer) \
  EFI_IP4_EQUAL (&(Offer)->Dhcp4.Header.YourAddr, &mZeroIp4Addr)

#define IS_DISABLE_BCAST_DISCOVER(x) \
  (((x) & BIT (0)) == BIT (0))

#define IS_DISABLE_MCAST_DISCOVER(x) \
  (((x) & BIT (1)) == BIT (1))

#define IS_ENABLE_USE_SERVER_LIST(x) \
  (((x) & BIT (2)) == BIT (2))

#define IS_DISABLE_PROMPT_MENU(x) \
  (((x) & BIT (3)) == BIT (3))

#pragma pack(1)
typedef struct {
  UINT8    ParaList[135];
} PXEBC_DHCP4_OPTION_PARA;

typedef struct {
  UINT16    Size;
} PXEBC_DHCP4_OPTION_MAX_MESG_SIZE;

typedef struct {
  UINT8    Type;
  UINT8    MajorVer;
  UINT8    MinorVer;
} PXEBC_DHCP4_OPTION_UNDI;

typedef struct {
  UINT8    Type;
} PXEBC_DHCP4_OPTION_MESG;

typedef struct {
  UINT16    Type;
} PXEBC_DHCP4_OPTION_ARCH;

typedef struct {
  UINT8    ClassIdentifier[10];
  UINT8    ArchitecturePrefix[5];
  UINT8    ArchitectureType[5];
  UINT8    Lit3[1];
  UINT8    InterfaceName[4];
  UINT8    Lit4[1];
  UINT8    UndiMajor[3];
  UINT8    UndiMinor[3];
} PXEBC_DHCP4_OPTION_CLID;

typedef struct {
  UINT8    Type;
  UINT8    Guid[16];
} PXEBC_DHCP4_OPTION_UUID;

typedef struct {
  UINT16    Type;
  UINT16    Layer;
} PXEBC_OPTION_BOOT_ITEM;

#pragma pack()

typedef union {
  PXEBC_DHCP4_OPTION_PARA             *Para;
  PXEBC_DHCP4_OPTION_UNDI             *Undi;
  PXEBC_DHCP4_OPTION_ARCH             *Arch;
  PXEBC_DHCP4_OPTION_CLID             *Clid;
  PXEBC_DHCP4_OPTION_UUID             *Uuid;
  PXEBC_DHCP4_OPTION_MESG             *Mesg;
  PXEBC_DHCP4_OPTION_MAX_MESG_SIZE    *MaxMesgSize;
} PXEBC_DHCP4_OPTION_ENTRY;

#pragma pack(1)
typedef struct {
  UINT16              Type;
  UINT8               IpCnt;
  EFI_IPv4_ADDRESS    IpAddr[1];
} PXEBC_BOOT_SVR_ENTRY;

typedef struct {
  UINT16    Type;
  UINT8     DescLen;
  UINT8     DescStr[1];
} PXEBC_BOOT_MENU_ENTRY;

typedef struct {
  UINT8    Timeout;
  UINT8    Prompt[1];
} PXEBC_MENU_PROMPT;
#pragma pack()

typedef struct {
  UINT32                   BitMap[8];
  EFI_IPv4_ADDRESS         MtftpIp;
  UINT16                   MtftpCPort;
  UINT16                   MtftpSPort;
  UINT8                    MtftpTimeout;
  UINT8                    MtftpDelay;
  UINT8                    DiscoverCtrl;
  EFI_IPv4_ADDRESS         DiscoverMcastIp;
  EFI_IPv4_ADDRESS         McastIpBase;
  UINT16                   McastIpBlock;
  UINT16                   McastIpRange;
  UINT16                   BootSrvType;
  UINT16                   BootSrvLayer;
  PXEBC_BOOT_SVR_ENTRY     *BootSvr;
  UINT8                    BootSvrLen;
  PXEBC_BOOT_MENU_ENTRY    *BootMenu;
  UINT8                    BootMenuLen;
  PXEBC_MENU_PROMPT        *MenuPrompt;
  UINT8                    MenuPromptLen;
  UINT32                   *CredType;
  UINT8                    CredTypeLen;
} PXEBC_VENDOR_OPTION;

#define PXEBC_CACHED_DHCP4_PACKET_MAX_SIZE  (OFFSET_OF (EFI_DHCP4_PACKET, Dhcp4) + PXEBC_DHCP4_PACKET_MAX_SIZE)

typedef union {
  EFI_DHCP4_PACKET    Offer;
  EFI_DHCP4_PACKET    Ack;
  UINT8               Buffer[PXEBC_CACHED_DHCP4_PACKET_MAX_SIZE];
} PXEBC_DHCP4_PACKET;

typedef struct {
  PXEBC_DHCP4_PACKET         Packet;
  PXEBC_OFFER_TYPE           OfferType;
  EFI_DHCP4_PACKET_OPTION    *OptList[PXEBC_DHCP4_TAG_INDEX_MAX];
  PXEBC_VENDOR_OPTION        VendorOpt;
} PXEBC_DHCP4_PACKET_CACHE;

/**
  Create a template DHCPv4 packet as a seed.

  @param[out] Seed           Pointer to the seed packet.
  @param[in]  Udp4           Pointer to EFI_UDP4_PROTOCOL.

**/
VOID
PxeBcSeedDhcp4Packet (
  OUT EFI_DHCP4_PACKET   *Seed,
  IN  EFI_UDP4_PROTOCOL  *Udp4
  );

/**
  Parse the cached DHCPv4 packet, including all the options.

  @param[in]  Cache4             Pointer to cached DHCPv4 packet.

  @retval     EFI_SUCCESS        Parsed the DHCPv4 packet successfully.
  @retval     EFI_DEVICE_ERROR   Failed to parse and invalid packet.

**/
EFI_STATUS
PxeBcParseDhcp4Packet (
  IN PXEBC_DHCP4_PACKET_CACHE  *Cache4
  );

/**
  Build and send out the request packet for the bootfile, and parse the reply.

  @param[in]  Private               Pointer to PxeBc private data.
  @param[in]  Type                  PxeBc option boot item type.
  @param[in]  Layer                 Pointer to option boot item layer.
  @param[in]  UseBis                Use BIS or not.
  @param[in]  DestIp                Pointer to the server address.
  @param[in]  IpCount               The total count of the server address.
  @param[in]  SrvList               Pointer to EFI_PXE_BASE_CODE_SRVLIST.

  @retval     EFI_SUCCESS           Successfully discovered boot file.
  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resource.
  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.
  @retval     Others                Failed to discover boot file.

**/
EFI_STATUS
PxeBcDhcp4Discover (
  IN  PXEBC_PRIVATE_DATA         *Private,
  IN  UINT16                     Type,
  IN  UINT16                     *Layer,
  IN  BOOLEAN                    UseBis,
  IN  EFI_IP_ADDRESS             *DestIp,
  IN  UINT16                     IpCount,
  IN  EFI_PXE_BASE_CODE_SRVLIST  *SrvList
  );

/**
  Switch the Ip4 policy to static.

  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.

  @retval     EFI_SUCCESS         The policy is already configured to static.
  @retval     Others              Other error as indicated..

**/
EFI_STATUS
PxeBcSetIp4Policy (
  IN PXEBC_PRIVATE_DATA  *Private
  );

/**
  Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information.

  @param[in]  Private           Pointer to PxeBc private data.
  @param[in]  Dhcp4             Pointer to the EFI_DHCP4_PROTOCOL

  @retval EFI_SUCCESS           The D.O.R.A process successfully finished.
  @retval Others                Failed to finish the D.O.R.A process.

**/
EFI_STATUS
PxeBcDhcp4Dora (
  IN PXEBC_PRIVATE_DATA  *Private,
  IN EFI_DHCP4_PROTOCOL  *Dhcp4
  );
