/*! @file i386/libsaio/pxe.h @copyright 2007,2009 David F. Elliott @abstract Basic PXE Interface @discussion Functions and structures for making low-level PXE API calls. The structures and typedefs are based very heavily on the PXE Specification v 2.1. In most cases they have been copied verbatim from the document. */ #ifndef _I386_BOOT1PXE_PXE_H__ #define _I386_BOOT1PXE_PXE_H__ //========================================================================== /*! @enum PXENV_EXIT @abstract PXENV API Exit codes @discussion Only success or failure is reported. This is the return value of the pxeCall function. If it is PXENV_EXIT_SUCCESS then you may examine the command buffer (the second argument to pxeCall) for it's Status field to determine whether or not the command itself succeeded. */ typedef uint16_t PXENV_EXIT; #define PXENV_EXIT_SUCCESS 0x0000 #define PXENV_EXIT_FAILURE 0x0001 /*! @enum PXENV_STATUS @abstract PXENV command status codes @discussion This is the value of the Status field in the command buffer after having made a successful (in the sense that it was valid) API call. The command itself may or may not have succeeded which is what this status is for. */ typedef uint16_t PXENV_STATUS; #define PXENV_STATUS_SUCCESS 0x0000 #define IP_ADDR_LEN 4 typedef union u_IP4 { uint32_t num; uint8_t array[IP_ADDR_LEN]; } IP4; #define MAC_ADDR_LEN 16 typedef uint8_t MAC_ADDR[MAC_ADDR_LEN]; /*! @typdef UDP_PORT @abstract UDP port (typically of TFTP server) @discussion UDP port in network (big endian) byte order. */ typedef uint16_t UDP_PORT; typedef uint16_t OFF16; typedef uint16_t SEGSEL; typedef struct s_SEGOFF16 { OFF16 offset; SEGSEL segment; } __attribute__((packed)) SEGOFF16; typedef struct s_SEGDESC { uint16_t segment_address; uint32_t Physical_address; uint16_t Seg_Size; } __attribute__((packed)) t_SEGDESC; #define PXENV_GET_CACHED_INFO 0x71 typedef struct s_PXENV_GET_CACHED_INFO { PXENV_STATUS Status; uint16_t PacketType; #define PXENV_PACKET_TYPE_DHCP_DISCOVER 1 #define PXENV_PACKET_TYPE_DHCP_ACK 2 #define PXENV_PACKET_TYPE_CACHED_REPLY 3 uint16_t BufferSize; SEGOFF16 Buffer; uint16_t BufferLimit; } __attribute__((packed)) t_PXENV_GET_CACHED_INFO; #define PXENV_TFTP_OPEN 0x0020 #define PXENV_TFTP_CLOSE 0x0021 #define PXENV_TFTP_READ 0x0022 #define PXENV_TFTP_GET_FSIZE 0x0025 typedef struct s_PXENV_TFTP_OPEN { PXENV_STATUS Status; IP4 ServerIPAddress; IP4 GatewayIPAddress; char FileName[128]; UDP_PORT TFTPPort; uint16_t PacketSize; } __attribute__((packed)) t_PXENV_TFTP_OPEN; typedef struct s_PXENV_TFTP_READ { PXENV_STATUS Status; uint16_t PacketNumber; uint16_t BufferSize; SEGOFF16 Buffer; } __attribute__((packed)) t_PXENV_TFTP_READ; typedef struct s_PXENV_TFTP_CLOSE { PXENV_STATUS Status; } t_PXENV_TFTP_CLOSE; typedef struct s_PXENV_TFTP_GET_FSIZE { PXENV_STATUS Status; IP4 ServerIPAddress; IP4 GatewayIPAddress; char FileName[128]; uint32_t FileSize; } __attribute__((packed)) t_PXENV_TFTP_GET_FSIZE; typedef struct s_PXENV { uint8_t Signature[6]; uint16_t Version; uint8_t Length; uint8_t Checksum; SEGOFF16 RMEntry; uint32_t PMOffset; SEGSEL PMSelector; SEGSEL StackSeg; uint16_t StackSize; SEGSEL BC_CodeSeg; uint16_t BC_CodeSize; SEGSEL BC_DataSeg; uint16_t BC_DataSize; SEGSEL UNDIDataSeg; uint16_t UNDIDataSize; SEGSEL UNDICodeSeg; uint16_t UNDICodeSize; SEGOFF16 PXEPtr; } __attribute__((packed)) t_PXENV; typedef struct S_BANGPXE { uint8_t Signature[4]; uint8_t StructLength; uint8_t StructCksum; uint8_t StructRev; uint8_t reserved_1; SEGOFF16 UNDIROMID; SEGOFF16 BaseROMID; SEGOFF16 EntryPointSP; SEGOFF16 EntryPointESP; SEGOFF16 StatusCallout; uint8_t reserved_2; uint8_t SegDescCnt; SEGSEL FirstSelector; t_SEGDESC Stack; t_SEGDESC UNDIData; t_SEGDESC UNDICode; t_SEGDESC UNDICodeWrite; t_SEGDESC BC_Data; t_SEGDESC BC_Code; t_SEGDESC BC_CodeWrite; } __attribute__((packed)) t_BANGPXE; typedef struct bootph { uint8_t opcode; #define BOOTP_REQ 1 #define BOOTP_REP 2 uint8_t Hardware; uint8_t Hardlen; uint8_t Gatehops; uint32_t ident; uint16_t seconds; uint16_t Flags; #define BOOTP_BCAST 0x8000 IP4 cip; IP4 yip; IP4 sip; IP4 gip; MAC_ADDR CAddr; uint8_t Sname[64]; uint8_t bootfile[128]; union { struct s_BOOTP_DHCPVEND { uint8_t magic[4]; #define VM_RFC1048 0x63825363L uint32_t flags; uint8_t pad[56]; } __attribute__((packed)) v; uint8_t d[sizeof(struct s_BOOTP_DHCPVEND)]; } vendor; } __attribute__((packed)) BOOTPLAYER; #define PXENV_UNDI_GET_STATE 0x0015 typedef struct s_PXENV_UNDI_GET_STATE { #define PXE_UNDI_GET_STATE_STARTED 1 #define PXE_UNDI_GET_STATE_INITIALIZED 2 #define PXE_UNDI_GET_STATE_OPENED 3 PXENV_STATUS Status; uint8_t UNDIstate; } t_PXENV_UNDI_GET_STATE; /*! @abstract The BP upon entry from real-mode @discussion SS:pxe_real_entry_ebp should point to a real-mode frame containing previous BP, return IP, return CS */ uint32_t gPXE_real_entry_ebp; /*! @abstract Initialize PXE runtime from bootstrap info @discussion When booting far ptr [SS:SP + 4] will point to '!PXE' structure if bootstrapped from PXE and ES:BX will point to 'PXENV+' structure. However, when booting from disk these will basically have garbage in them. Due to the way the booter is entered there is no contextual information from prior stages about which stage was used. Therefore, this function can be called which will return true if either '!PXE' or 'PXENV+' was found to be valid. That means that PXE was found and it's highly likely that the user intends to boot from PXE. */ bool initPxeFromBootstrap(t_BANGPXE *bangpxe, t_PXENV *pxenv); /*! @function PXEStartup @abstract Starts up the PXE runtime. @discussion You must call this before calling any other functions. In the simplest case, the '!PXE' structure was already found on the stack, but if the global pointer to '!PXE' has not yet been set then this function will attempt to find it per the PXE spec. */ extern BOOL PXEStartup(); /*! @abstract DHCP reply packet @discussion Grabbed from PXE in PXEStartup. */ BOOTPLAYER *gDhcpAck; /*! @abstract Size of DHCP reply packet. Grabbed from PXE in PXEStartup. */ uint32_t gDhcpAckSize; /*! @abstract DHCP reply packet @discussion Grabbed from PXE in PXEStartup. */ BOOTPLAYER *gBootReply; /*! @abstract Base filename (e.g. /darwin/) @discussion During PXEStartup, the DHCP reply packet is parsed and the 128 byte bootfile field is parsed. Whatever is after the last '/' is lopped off leaving only the path to which other filenames can be appended. For example, if you booted /XYZ/boot1pxe then this will contain "/XYZ/". The boot1pxe loader iself will load "boot" relative to this directory and then boot will load mach_kernel and mach_kernel.mkext relative to this directory. */ char gPXE_BaseFilename[128]; /*! @function pxeCall @abstract Low-level PXE call. @discussion See section 3.2 of PXE Specification v 2.1 Makes a PXE call using the entrypoint from '!PXE' which is discovered by PXEStartup */ extern uint32_t pxeCall(uint16_t command, void *pData); /*! @function pxenvGetCachedInfo @abstract Invokes the get cached info command. */ extern uint16_t pxenvGetCachedInfo(t_PXENV_GET_CACHED_INFO *pGetCachedInfoCommand, uint16_t packetType); /*! @function getPxeCachedBuffer @abstract Translates the SEGOFF16 pointer in the output command structure to a linear pointer. */ extern void * getPxeCachedBuffer(t_PXENV_GET_CACHED_INFO *pGetCachedInfoCommand); /** * Allows the binary search result to be controlled * SEARCH_BEFORE * Returns the index of the item prior to the target * regardless of whether or not the target was found. * SEARCH_FIRST * If multiple entries exist for the target * return the index of the first entry. * SEARCH_ANY * Stops as soon as an entry is found * SEARCH_LAST * If multiple entries exist for the target * return the index of the last entry * SEARCH_AFTER * Returns the index of the item after the target * regardless of whether or not the target was found */ typedef enum search_disposition { SEARCH_BEFORE = -2 , SEARCH_FIRST = -1 , SEARCH_ANY = 0 , SEARCH_LAST = 1 , SEARCH_AFTER = 2 } search_disposition; /** * Simple binary search with controllable disposition * * Suppose an array BCEF * * If you look for A and you want the item before A you will get -1 whereas in all other * cases you will get 0 because the search will land on begin (which is B). * * If you look for G and you want the item before G you will get F because the search * will land on end and for SEARCH_BEFORE the behavior is end -1. In all other cases * you will get end. * * If you look for D what will happen is end will move to E and then C will be tested * which is less than and so it will move begin to E at which point the loop will * end landing on E. If you are search before you will get the index of C but in * all other cases you will get the index of E. * * In general the SEARCH_BEFORE disposition is not very useful. * * * key * The option being sought * base * The options array * end * The size of the options array * disp * The search disposition, see enum search_disposition * Return * The return value is dependent on the disposition. */ extern int binarySearchOptionWithTag(unsigned char key, unsigned char const *base[], int begin, int end, search_disposition disp); /** * Parses a dhcp options unsigned character array into a sorted (by option ID) list of pointers * to each option. * * For example, suppose the input looks like this: * off: typ len value * 0: 66 7 "example" * 9: 1 4 255 255 255 0 * 15: 67 6 "boot.0" * 23: 5 8 192 0 2 42 198 51 100 43 * * In that case the output (array of pointers) would be: * { base + 9, base + 23, base + 0, base + 15 } * * out_list * Array of output options * out_size * Complete size of output array * io_len * next free entry (input and output) * dhcp_options * The options octet array * options_sz * The size of the options octet array */ extern unsigned char const * parseVendorOptionBuffer(unsigned char const *out_list[], size_t out_size, size_t *io_len, unsigned char *popt52, unsigned char const *dhcp_options, size_t options_sz); /** * Parses the options out of the BOOTP packet if it is a DHCP packet. * * If it is a DHCP packet (has magic) then the options field will be interpreted. It is not legal * for a DHCP packet to have no options (it must at least have a type and a server identifier) and * even if it were it would logically be no different from a BOOTP packet so a return value of * 0 can be used as an indicator that this is a BOOTP packet or is effectively a BOOTP packet. */ extern size_t parseOptionsFromDhcpPacket(unsigned char const *option_list[], size_t option_list_size, BOOTPLAYER const *dhcp_ack_packet, size_t dhcp_ack_packet_sz); /** * Copies an option value into a destination buffer. * * DHCP options are octet strings, not necessarily C strings, so no NUL terminating byte is added. * When DHCP options appear multiple times the values are to be concatenated per RFC 2131. * * dst * The destination character buffer * dstsz * The maximum number of bytes to write to the destination buffer. * key * The DHCP option tag to copy * option_list * The complete list of options * start * The starting (inclusive) index into option_list * end * The ending (exclusive) index of option_list, i.e. the complete size. * * return * The number of bytes that would be copied. If ret <= dstsz then the bytes * were actually copied. Otherwise this is the size of the byte array you * would need to allocate to store the value. Keep in mind if your destination * needs to be NUL terminated you will need to add one. */ extern size_t copyOptionValueForKeyFromOptionList(void *dst, size_t dstsz, unsigned char key, unsigned char const *option_list[], size_t start, size_t end); /** * Sets the base filename into the destination buffer from the DHCP packet and computed option list * * Behavior is similar to strlcpy. That is, the destination buffer will always be NUL terminated * so long as dstsz > 0 and the required length is always returned. */ extern size_t setBaseFilenameFromOptions(char *dst, size_t dstsz, BOOTPLAYER const *dhcp_packet, unsigned char const *option_list[], size_t const option_list_len); extern void debugPrintOptions(unsigned char const *option_list[], size_t start, size_t end, BOOTPLAYER const *packet); #endif /* ndef _I386_BOOT1PXE_PXE_H__ */