Index: stand/efi/include/Protocol/Http.h =================================================================== --- /dev/null +++ stand/efi/include/Protocol/Http.h @@ -0,0 +1,523 @@ +/* $FreeBSD$ */ +/** @file + This file defines the EFI HTTP Protocol interface. It is split into + the following two main sections: + HTTP Service Binding Protocol (HTTPSB) + HTTP Protocol (HTTP) + + Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+ (C) Copyright 2015-2017 Hewlett Packard Enterprise Development LP
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + This Protocol is introduced in UEFI Specification 2.5 + +**/ + +#ifndef __EFI_HTTP_PROTOCOL_H__ +#define __EFI_HTTP_PROTOCOL_H__ + +#define EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID \ + { \ + 0xbdc8e6af, 0xd9bc, 0x4379, {0xa7, 0x2a, 0xe0, 0xc4, 0xe7, 0x5d, 0xae, 0x1c } \ + } + +#define EFI_HTTP_PROTOCOL_GUID \ + { \ + 0x7a59b29b, 0x910b, 0x4171, {0x82, 0x42, 0xa8, 0x5a, 0x0d, 0xf2, 0x5b, 0x5b } \ + } + +typedef struct _EFI_HTTP_PROTOCOL EFI_HTTP_PROTOCOL; + +/// +/// EFI_HTTP_VERSION +/// +typedef enum { + HttpVersion10, + HttpVersion11, + HttpVersionUnsupported +} EFI_HTTP_VERSION; + +/// +/// EFI_HTTP_METHOD +/// +typedef enum { + HttpMethodGet, + HttpMethodPost, + HttpMethodPatch, + HttpMethodOptions, + HttpMethodConnect, + HttpMethodHead, + HttpMethodPut, + HttpMethodDelete, + HttpMethodTrace, + HttpMethodMax +} EFI_HTTP_METHOD; + +/// +/// EFI_HTTP_STATUS_CODE +/// +typedef enum { + HTTP_STATUS_UNSUPPORTED_STATUS = 0, + HTTP_STATUS_100_CONTINUE, + HTTP_STATUS_101_SWITCHING_PROTOCOLS, + HTTP_STATUS_200_OK, + HTTP_STATUS_201_CREATED, + HTTP_STATUS_202_ACCEPTED, + HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION, + HTTP_STATUS_204_NO_CONTENT, + HTTP_STATUS_205_RESET_CONTENT, + HTTP_STATUS_206_PARTIAL_CONTENT, + HTTP_STATUS_300_MULTIPLE_CHOICES, + HTTP_STATUS_301_MOVED_PERMANENTLY, + HTTP_STATUS_302_FOUND, + HTTP_STATUS_303_SEE_OTHER, + HTTP_STATUS_304_NOT_MODIFIED, + HTTP_STATUS_305_USE_PROXY, + HTTP_STATUS_307_TEMPORARY_REDIRECT, + HTTP_STATUS_400_BAD_REQUEST, + HTTP_STATUS_401_UNAUTHORIZED, + HTTP_STATUS_402_PAYMENT_REQUIRED, + HTTP_STATUS_403_FORBIDDEN, + HTTP_STATUS_404_NOT_FOUND, + HTTP_STATUS_405_METHOD_NOT_ALLOWED, + HTTP_STATUS_406_NOT_ACCEPTABLE, + HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED, + HTTP_STATUS_408_REQUEST_TIME_OUT, + HTTP_STATUS_409_CONFLICT, + HTTP_STATUS_410_GONE, + HTTP_STATUS_411_LENGTH_REQUIRED, + HTTP_STATUS_412_PRECONDITION_FAILED, + HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE, + HTTP_STATUS_414_REQUEST_URI_TOO_LARGE, + HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE, + HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED, + HTTP_STATUS_417_EXPECTATION_FAILED, + HTTP_STATUS_500_INTERNAL_SERVER_ERROR, + HTTP_STATUS_501_NOT_IMPLEMENTED, + HTTP_STATUS_502_BAD_GATEWAY, + HTTP_STATUS_503_SERVICE_UNAVAILABLE, + HTTP_STATUS_504_GATEWAY_TIME_OUT, + HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED, + HTTP_STATUS_308_PERMANENT_REDIRECT +} EFI_HTTP_STATUS_CODE; + +/// +/// EFI_HTTPv4_ACCESS_POINT +/// +typedef struct { + /// + /// Set to TRUE to instruct the EFI HTTP instance to use the default address + /// information in every TCP connection made by this instance. In addition, when set + /// to TRUE, LocalAddress and LocalSubnet are ignored. + /// + BOOLEAN UseDefaultAddress; + /// + /// If UseDefaultAddress is set to FALSE, this defines the local IP address to be + /// used in every TCP connection opened by this instance. + /// + EFI_IPv4_ADDRESS LocalAddress; + /// + /// If UseDefaultAddress is set to FALSE, this defines the local subnet to be used + /// in every TCP connection opened by this instance. + /// + EFI_IPv4_ADDRESS LocalSubnet; + /// + /// This defines the local port to be used in + /// every TCP connection opened by this instance. + /// + UINT16 LocalPort; +} EFI_HTTPv4_ACCESS_POINT; + +/// +/// EFI_HTTPv6_ACCESS_POINT +/// +typedef struct { + /// + /// Local IP address to be used in every TCP connection opened by this instance. + /// + EFI_IPv6_ADDRESS LocalAddress; + /// + /// Local port to be used in every TCP connection opened by this instance. + /// + UINT16 LocalPort; +} EFI_HTTPv6_ACCESS_POINT; + +/// +/// EFI_HTTP_CONFIG_DATA_ACCESS_POINT +/// + + +typedef struct { + /// + /// HTTP version that this instance will support. + /// + EFI_HTTP_VERSION HttpVersion; + /// + /// Time out (in milliseconds) when blocking for requests. + /// + UINT32 TimeOutMillisec; + /// + /// Defines behavior of EFI DNS and TCP protocols consumed by this instance. If + /// FALSE, this instance will use EFI_DNS4_PROTOCOL and EFI_TCP4_PROTOCOL. If TRUE, + /// this instance will use EFI_DNS6_PROTOCOL and EFI_TCP6_PROTOCOL. + /// + BOOLEAN LocalAddressIsIPv6; + + union { + /// + /// When LocalAddressIsIPv6 is FALSE, this points to the local address, subnet, and + /// port used by the underlying TCP protocol. + /// + EFI_HTTPv4_ACCESS_POINT *IPv4Node; + /// + /// When LocalAddressIsIPv6 is TRUE, this points to the local IPv6 address and port + /// used by the underlying TCP protocol. + /// + EFI_HTTPv6_ACCESS_POINT *IPv6Node; + } AccessPoint; +} EFI_HTTP_CONFIG_DATA; + +/// +/// EFI_HTTP_REQUEST_DATA +/// +typedef struct { + /// + /// The HTTP method (e.g. GET, POST) for this HTTP Request. + /// + EFI_HTTP_METHOD Method; + /// + /// The URI of a remote host. From the information in this field, the HTTP instance + /// will be able to determine whether to use HTTP or HTTPS and will also be able to + /// determine the port number to use. If no port number is specified, port 80 (HTTP) + /// is assumed. See RFC 3986 for more details on URI syntax. + /// + CHAR16 *Url; +} EFI_HTTP_REQUEST_DATA; + +/// +/// EFI_HTTP_RESPONSE_DATA +/// +typedef struct { + /// + /// Response status code returned by the remote host. + /// + EFI_HTTP_STATUS_CODE StatusCode; +} EFI_HTTP_RESPONSE_DATA; + +/// +/// EFI_HTTP_HEADER +/// +typedef struct { + /// + /// Null terminated string which describes a field name. See RFC 2616 Section 14 for + /// detailed information about field names. + /// + CHAR8 *FieldName; + /// + /// Null terminated string which describes the corresponding field value. See RFC 2616 + /// Section 14 for detailed information about field values. + /// + CHAR8 *FieldValue; +} EFI_HTTP_HEADER; + +/// +/// EFI_HTTP_MESSAGE +/// +typedef struct { + /// + /// HTTP message data. + /// + union { + /// + /// When the token is used to send a HTTP request, Request is a pointer to storage that + /// contains such data as URL and HTTP method. + /// + EFI_HTTP_REQUEST_DATA *Request; + /// + /// When used to await a response, Response points to storage containing HTTP response + /// status code. + /// + EFI_HTTP_RESPONSE_DATA *Response; + } Data; + /// + /// Number of HTTP header structures in Headers list. On request, this count is + /// provided by the caller. On response, this count is provided by the HTTP driver. + /// + UINTN HeaderCount; + /// + /// Array containing list of HTTP headers. On request, this array is populated by the + /// caller. On response, this array is allocated and populated by the HTTP driver. It + /// is the responsibility of the caller to free this memory on both request and + /// response. + /// + EFI_HTTP_HEADER *Headers; + /// + /// Length in bytes of the HTTP body. This can be zero depending on the HttpMethod type. + /// + UINTN BodyLength; + /// + /// Body associated with the HTTP request or response. This can be NULL depending on + /// the HttpMethod type. + /// + VOID *Body; +} EFI_HTTP_MESSAGE; + + +/// +/// EFI_HTTP_TOKEN +/// +typedef struct { + /// + /// This Event will be signaled after the Status field is updated by the EFI HTTP + /// Protocol driver. The type of Event must be EFI_NOTIFY_SIGNAL. The Task Priority + /// Level (TPL) of Event must be lower than or equal to TPL_CALLBACK. + /// + EFI_EVENT Event; + /// + /// Status will be set to one of the following value if the HTTP request is + /// successfully sent or if an unexpected error occurs: + /// EFI_SUCCESS: The HTTP request was successfully sent to the remote host. + /// EFI_HTTP_ERROR: The response message was successfully received but contains a + /// HTTP error. The response status code is returned in token. + /// EFI_ABORTED: The HTTP request was cancelled by the caller and removed from + /// the transmit queue. + /// EFI_TIMEOUT: The HTTP request timed out before reaching the remote host. + /// EFI_DEVICE_ERROR: An unexpected system or network error occurred. + /// + EFI_STATUS Status; + /// + /// Pointer to storage containing HTTP message data. + /// + EFI_HTTP_MESSAGE *Message; +} EFI_HTTP_TOKEN; + +/** + Returns the operational parameters for the current HTTP child instance. + + The GetModeData() function is used to read the current mode data (operational + parameters) for this HTTP protocol instance. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[out] HttpConfigData Point to buffer for operational parameters of this + HTTP instance. It is the responsibility of the caller + to allocate the memory for HttpConfigData and + HttpConfigData->AccessPoint.IPv6Node/IPv4Node. In fact, + it is recommended to allocate sufficient memory to record + IPv6Node since it is big enough for all possibilities. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_INVALID_PARAMETER This is NULL. + HttpConfigData is NULL. + HttpConfigData->AccessPoint.IPv4Node or + HttpConfigData->AccessPoint.IPv6Node is NULL. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_HTTP_GET_MODE_DATA)( + IN EFI_HTTP_PROTOCOL *This, + OUT EFI_HTTP_CONFIG_DATA *HttpConfigData + ); + +/** + Initialize or brutally reset the operational parameters for this EFI HTTP instance. + + The Configure() function does the following: + When HttpConfigData is not NULL Initialize this EFI HTTP instance by configuring + timeout, local address, port, etc. + When HttpConfigData is NULL, reset this EFI HTTP instance by closing all active + connections with remote hosts, canceling all asynchronous tokens, and flush request + and response buffers without informing the appropriate hosts. + + No other EFI HTTP function can be executed by this instance until the Configure() + function is executed and returns successfully. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] HttpConfigData Pointer to the configure data to configure the instance. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + HttpConfigData->LocalAddressIsIPv6 is FALSE and + HttpConfigData->AccessPoint.IPv4Node is NULL. + HttpConfigData->LocalAddressIsIPv6 is TRUE and + HttpConfigData->AccessPoint.IPv6Node is NULL. + @retval EFI_ALREADY_STARTED Reinitialize this HTTP instance without calling + Configure() with NULL to reset it. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when + executing Configure(). + @retval EFI_UNSUPPORTED One or more options in ConfigData are not supported + in the implementation. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_HTTP_CONFIGURE)( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_CONFIG_DATA *HttpConfigData OPTIONAL + ); + +/** + The Request() function queues an HTTP request to this HTTP instance, + similar to Transmit() function in the EFI TCP driver. When the HTTP request is sent + successfully, or if there is an error, Status in token will be updated and Event will + be signaled. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Pointer to storage containing HTTP request token. + + @retval EFI_SUCCESS Outgoing data was processed. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit or receive queue. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + Token is NULL. + Token->Message is NULL. + Token->Message->Body is not NULL, + Token->Message->BodyLength is non-zero, and + Token->Message->Data is NULL, but a previous call to + Request()has not been completed successfully. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + @retval EFI_UNSUPPORTED The HTTP method is not supported in current implementation. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_HTTP_REQUEST) ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ); + +/** + Abort an asynchronous HTTP request or response token. + + The Cancel() function aborts a pending HTTP request or response transaction. If + Token is not NULL and the token is in transmit or receive queues when it is being + cancelled, its Token->Status will be set to EFI_ABORTED and then Token->Event will + be signaled. If the token is not in one of the queues, which usually means that the + asynchronous operation has completed, EFI_NOT_FOUND is returned. If Token is NULL, + all asynchronous tokens issued by Request() or Response() will be aborted. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Point to storage containing HTTP request or response + token. + + @retval EFI_SUCCESS Request and Response queues are successfully flushed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance hasn't been configured. + @retval EFI_NOT_FOUND The asynchronous request or response token is not + found. + @retval EFI_UNSUPPORTED The implementation does not support this function. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_HTTP_CANCEL)( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ); + +/** + The Response() function queues an HTTP response to this HTTP instance, similar to + Receive() function in the EFI TCP driver. When the HTTP Response is received successfully, + or if there is an error, Status in token will be updated and Event will be signaled. + + The HTTP driver will queue a receive token to the underlying TCP instance. When data + is received in the underlying TCP instance, the data will be parsed and Token will + be populated with the response data. If the data received from the remote host + contains an incomplete or invalid HTTP header, the HTTP driver will continue waiting + (asynchronously) for more data to be sent from the remote host before signaling + Event in Token. + + It is the responsibility of the caller to allocate a buffer for Body and specify the + size in BodyLength. If the remote host provides a response that contains a content + body, up to BodyLength bytes will be copied from the receive buffer into Body and + BodyLength will be updated with the amount of bytes received and copied to Body. This + allows the client to download a large file in chunks instead of into one contiguous + block of memory. Similar to HTTP request, if Body is not NULL and BodyLength is + non-zero and all other fields are NULL or 0, the HTTP driver will queue a receive + token to underlying TCP instance. If data arrives in the receive buffer, up to + BodyLength bytes of data will be copied to Body. The HTTP driver will then update + BodyLength with the amount of bytes received and copied to Body. + + If the HTTP driver does not have an open underlying TCP connection with the host + specified in the response URL, Request() will return EFI_ACCESS_DENIED. This is + consistent with RFC 2616 recommendation that HTTP clients should attempt to maintain + an open TCP connection between client and host. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Pointer to storage containing HTTP response token. + + @retval EFI_SUCCESS Allocation succeeded. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been + initialized. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + Token is NULL. + Token->Message->Headers is NULL. + Token->Message is NULL. + Token->Message->Body is not NULL, + Token->Message->BodyLength is non-zero, and + Token->Message->Data is NULL, but a previous call to + Response() has not been completed successfully. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + @retval EFI_ACCESS_DENIED An open TCP connection is not present with the host + specified by response URL. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_HTTP_RESPONSE) ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ); + +/** + The Poll() function can be used by network drivers and applications to increase the + rate that data packets are moved between the communication devices and the transmit + and receive queues. + + In some systems, the periodic timer event in the managed network driver may not poll + the underlying communications device fast enough to transmit and/or receive all data + packets without missing incoming packets or dropping outgoing packets. Drivers and + applications that are experiencing packet loss should try calling the Poll() function + more often. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed.. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_READY No incoming or outgoing data is processed. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_HTTP_POLL) ( + IN EFI_HTTP_PROTOCOL *This + ); + +/// +/// The EFI HTTP protocol is designed to be used by EFI drivers and applications to +/// create and transmit HTTP Requests, as well as handle HTTP responses that are +/// returned by a remote host. This EFI protocol uses and relies on an underlying EFI +/// TCP protocol. +/// +struct _EFI_HTTP_PROTOCOL { + EFI_HTTP_GET_MODE_DATA GetModeData; + EFI_HTTP_CONFIGURE Configure; + EFI_HTTP_REQUEST Request; + EFI_HTTP_CANCEL Cancel; + EFI_HTTP_RESPONSE Response; + EFI_HTTP_POLL Poll; +}; + +extern EFI_GUID gEfiHttpServiceBindingProtocolGuid; +extern EFI_GUID gEfiHttpProtocolGuid; + +#endif Index: stand/efi/include/Protocol/Ip4Config2.h =================================================================== --- /dev/null +++ stand/efi/include/Protocol/Ip4Config2.h @@ -0,0 +1,324 @@ +/* $FreeBSD$ */ +/** @file + This file provides a definition of the EFI IPv4 Configuration II + Protocol. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +@par Revision Reference: +This Protocol is introduced in UEFI Specification 2.5 + +**/ +#ifndef __EFI_IP4CONFIG2_PROTOCOL_H__ +#define __EFI_IP4CONFIG2_PROTOCOL_H__ + +/* #include */ + +#define EFI_IP4_CONFIG2_PROTOCOL_GUID \ + { \ + 0x5b446ed1, 0xe30b, 0x4faa, {0x87, 0x1a, 0x36, 0x54, 0xec, 0xa3, 0x60, 0x80 } \ + } + +typedef struct _EFI_IP4_CONFIG2_PROTOCOL EFI_IP4_CONFIG2_PROTOCOL; + + +/// +/// EFI_IP4_CONFIG2_DATA_TYPE +/// +typedef enum { + /// + /// The interface information of the communication device this EFI + /// IPv4 Configuration II Protocol instance manages. This type of + /// data is read only. The corresponding Data is of type + /// EFI_IP4_CONFIG2_INTERFACE_INFO. + /// + Ip4Config2DataTypeInterfaceInfo, + /// + /// The general configuration policy for the EFI IPv4 network stack + /// running on the communication device this EFI IPv4 + /// Configuration II Protocol instance manages. The policy will + /// affect other configuration settings. The corresponding Data is of + /// type EFI_IP4_CONFIG2_POLICY. + /// + Ip4Config2DataTypePolicy, + /// + /// The station addresses set manually for the EFI IPv4 network + /// stack. It is only configurable when the policy is + /// Ip4Config2PolicyStatic. The corresponding Data is of + /// type EFI_IP4_CONFIG2_MANUAL_ADDRESS. When DataSize + /// is 0 and Data is NULL, the existing configuration is cleared + /// from the EFI IPv4 Configuration II Protocol instance. + /// + Ip4Config2DataTypeManualAddress, + /// + /// The gateway addresses set manually for the EFI IPv4 network + /// stack running on the communication device this EFI IPv4 + /// Configuration II Protocol manages. It is not configurable when + /// the policy is Ip4Config2PolicyDhcp. The gateway + /// addresses must be unicast IPv4 addresses. The corresponding + /// Data is a pointer to an array of EFI_IPv4_ADDRESS instances. + /// When DataSize is 0 and Data is NULL, the existing configuration + /// is cleared from the EFI IPv4 Configuration II Protocol instance. + /// + Ip4Config2DataTypeGateway, + /// + /// The DNS server list for the EFI IPv4 network stack running on + /// the communication device this EFI IPv4 Configuration II + /// Protocol manages. It is not configurable when the policy is + /// Ip4Config2PolicyDhcp. The DNS server addresses must be + /// unicast IPv4 addresses. The corresponding Data is a pointer to + /// an array of EFI_IPv4_ADDRESS instances. When DataSize + /// is 0 and Data is NULL, the existing configuration is cleared + /// from the EFI IPv4 Configuration II Protocol instance. + /// + Ip4Config2DataTypeDnsServer, + Ip4Config2DataTypeMaximum +} EFI_IP4_CONFIG2_DATA_TYPE; + +/// +/// EFI_IP4_CONFIG2_INTERFACE_INFO related definitions +/// +#define EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE 32 + +/// +/// EFI_IP4_CONFIG2_INTERFACE_INFO +/// +typedef struct { + /// + /// The name of the interface. It is a NULL-terminated Unicode string. + /// + CHAR16 Name[EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE]; + /// + /// The interface type of the network interface. See RFC 1700, + /// section "Number Hardware Type". + /// + UINT8 IfType; + /// + /// The size, in bytes, of the network interface's hardware address. + /// + UINT32 HwAddressSize; + /// + /// The hardware address for the network interface. + /// + EFI_MAC_ADDRESS HwAddress; + /// + /// The station IPv4 address of this EFI IPv4 network stack. + /// + EFI_IPv4_ADDRESS StationAddress; + /// + /// The subnet address mask that is associated with the station address. + /// + EFI_IPv4_ADDRESS SubnetMask; + /// + /// Size of the following RouteTable, in bytes. May be zero. + /// + UINT32 RouteTableSize; + /// + /// The route table of the IPv4 network stack runs on this interface. + /// Set to NULL if RouteTableSize is zero. Type EFI_IP4_ROUTE_TABLE is defined in + /// EFI_IP4_PROTOCOL.GetModeData(). + /// + EFI_IP4_ROUTE_TABLE *RouteTable OPTIONAL; +} EFI_IP4_CONFIG2_INTERFACE_INFO; + +/// +/// EFI_IP4_CONFIG2_POLICY +/// +typedef enum { + /// + /// Under this policy, the Ip4Config2DataTypeManualAddress, + /// Ip4Config2DataTypeGateway and Ip4Config2DataTypeDnsServer configuration + /// data are required to be set manually. The EFI IPv4 Protocol will get all + /// required configuration such as IPv4 address, subnet mask and + /// gateway settings from the EFI IPv4 Configuration II protocol. + /// + Ip4Config2PolicyStatic, + /// + /// Under this policy, the Ip4Config2DataTypeManualAddress, + /// Ip4Config2DataTypeGateway and Ip4Config2DataTypeDnsServer configuration data are + /// not allowed to set via SetData(). All of these configurations are retrieved from DHCP + /// server or other auto-configuration mechanism. + /// + Ip4Config2PolicyDhcp, + Ip4Config2PolicyMax +} EFI_IP4_CONFIG2_POLICY; + +/// +/// EFI_IP4_CONFIG2_MANUAL_ADDRESS +/// +typedef struct { + /// + /// The IPv4 unicast address. + /// + EFI_IPv4_ADDRESS Address; + /// + /// The subnet mask. + /// + EFI_IPv4_ADDRESS SubnetMask; +} EFI_IP4_CONFIG2_MANUAL_ADDRESS; + +/** + Set the configuration for the EFI IPv4 network stack running on the communication device this EFI + IPv4 Configuration II Protocol instance manages. + + This function is used to set the configuration data of type DataType for the EFI IPv4 network stack + running on the communication device this EFI IPv4 Configuration II Protocol instance manages. + The successfully configured data is valid after system reset or power-off. + The DataSize is used to calculate the count of structure instances in the Data for some + DataType that multiple structure instances are allowed. + This function is always non-blocking. When setting some typeof configuration data, an + asynchronous process is invoked to check the correctness of the data, such as doing address conflict + detection on the manually set local IPv4 address. EFI_NOT_READY is returned immediately to + indicate that such an asynchronous process is invoked and the process is not finished yet. The caller + willing to get the result of the asynchronous process is required to call RegisterDataNotify() + to register an event on the specified configuration data. Once the event is signaled, the caller can call + GetData()to get back the configuration data in order to know the result. For other types of + configuration data that do not require an asynchronous configuration process, the result of the + operation is immediately returned. + + @param[in] This Pointer to the EFI_IP4_CONFIG2_PROTOCOL instance. + @param[in] DataType The type of data to set. + @param[in] DataSize Size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set. The type ofthe data buffer is associated + with the DataType. + + @retval EFI_SUCCESS The specified configuration data for the EFI IPv4 network stack is set + successfully. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + This is NULL. + One or more fields in Data and DataSize do not match the + requirement of the data type indicated by DataType. + @retval EFI_WRITE_PROTECTED The specified configuration data is read-only or the specified configuration + data can not be set under the current policy. + @retval EFI_ACCESS_DENIED Another set operation on the specified configuration data is already in process. + @retval EFI_NOT_READY An asynchronous process is invoked to set the specified configuration data and + the process is not finished yet. + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type indicated by DataType. + @retval EFI_UNSUPPORTED This DataType is not supported. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected system error or network error occurred. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IP4_CONFIG2_SET_DATA) ( + IN EFI_IP4_CONFIG2_PROTOCOL *This, + IN EFI_IP4_CONFIG2_DATA_TYPE DataType, + IN UINTN DataSize, + IN VOID *Data + ); + +/** + Get the configuration data for the EFI IPv4 network stack running on the communication device this + EFI IPv4 Configuration II Protocol instance manages. + + This function returns the configuration data of type DataType for the EFI IPv4 network stack + running on the communication device this EFI IPv4 Configuration II Protocol instance manages. + The caller is responsible for allocating the buffer usedto return the specified configuration data and + the required size will be returned to the caller if the size of the buffer is too small. + EFI_NOT_READY is returned if the specified configuration data is not ready due to an already in + progress asynchronous configuration process. The caller can call RegisterDataNotify() to + register an event on the specified configuration data. Once the asynchronous configuration process is + finished, the event will be signaled and a subsequent GetData() call will return the specified + configuration data. + + @param[in] This Pointer to the EFI_IP4_CONFIG2_PROTOCOL instance. + @param[in] DataType The type of data to get. + @param[out] DataSize On input, in bytes, the size of Data. On output, in bytes, the size + of buffer required to store the specified configuration data. + @param[in] Data The data buffer in which the configuration data is returned. The + type of the data buffer is associated with the DataType. Ignored + if DataSize is 0. + + @retval EFI_SUCCESS The specified configuration data is got successfully. + @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE: + This is NULL. + DataSize is NULL. + Data is NULL if *DataSizeis not zero. + @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified configuration data + and the required size is returned in DataSize. + @retval EFI_NOT_READY The specified configuration data is not ready due to an already in + progress asynchronous configuration process. + @retval EFI_NOT_FOUND The specified configuration data is not found. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IP4_CONFIG2_GET_DATA) ( + IN EFI_IP4_CONFIG2_PROTOCOL *This, + IN EFI_IP4_CONFIG2_DATA_TYPE DataType, + IN OUT UINTN *DataSize, + IN VOID *Data OPTIONAL + ); + +/** + Register an event that is to be signaled whenever a configuration process on the specified + configuration data is done. + + This function registers an event that is to be signaled whenever a configuration process on the + specified configuration data is done. An event can be registered for different DataType + simultaneously and the caller is responsible for determining which type of configuration data causes + the signaling of the event in such case. + + @param[in] This Pointer to the EFI_IP4_CONFIG2_PROTOCOL instance. + @param[in] DataType The type of data to unregister the event for. + @param[in] Event The event to register. + + @retval EFI_SUCCESS The notification event for the specified configuration data is + registered. + @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL. + @retval EFI_UNSUPPORTED The configuration data type specified by DataType is not supported. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_ACCESS_DENIED The Event is already registered for the DataType. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IP4_CONFIG2_REGISTER_NOTIFY) ( + IN EFI_IP4_CONFIG2_PROTOCOL *This, + IN EFI_IP4_CONFIG2_DATA_TYPE DataType, + IN EFI_EVENT Event + ); + +/** + Remove a previously registered event for the specified configuration data. + + This function removes a previously registeredevent for the specified configuration data. + + @param[in] This Pointer to the EFI_IP4_CONFIG2_PROTOCOL instance. + @param[in] DataType The type of data to remove the previously registered event for. + @param[in] Event The event to unregister. + + @retval EFI_SUCCESS The event registered for the specified configuration data is removed. + @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL. + @retval EFI_NOT_FOUND The Eventhas not been registered for the specified DataType. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IP4_CONFIG2_UNREGISTER_NOTIFY) ( + IN EFI_IP4_CONFIG2_PROTOCOL *This, + IN EFI_IP4_CONFIG2_DATA_TYPE DataType, + IN EFI_EVENT Event + ); + +/// +/// The EFI_IP4_CONFIG2_PROTOCOL is designed to be the central repository for the common +/// configurations and the administrator configurable settings for the EFI IPv4 network stack. +/// An EFI IPv4 Configuration II Protocol instance will be installed on each communication device that +/// the EFI IPv4 network stack runs on. +/// +struct _EFI_IP4_CONFIG2_PROTOCOL { + EFI_IP4_CONFIG2_SET_DATA SetData; + EFI_IP4_CONFIG2_GET_DATA GetData; + EFI_IP4_CONFIG2_REGISTER_NOTIFY RegisterDataNotify; + EFI_IP4_CONFIG2_UNREGISTER_NOTIFY UnregisterDataNotify; +}; + +extern EFI_GUID gEfiIp4Config2ProtocolGuid; + +#endif + Index: stand/efi/include/Protocol/ServiceBinding.h =================================================================== --- /dev/null +++ stand/efi/include/Protocol/ServiceBinding.h @@ -0,0 +1,95 @@ +/* $FreeBSD$ */ +/** @file + UEFI Service Binding Protocol is defined in UEFI specification. + + The file defines the generic Service Binding Protocol functions. + It provides services that are required to create and destroy child + handles that support a given set of protocols. + + Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_SERVICE_BINDING_H__ +#define __EFI_SERVICE_BINDING_H__ + +/// +/// Forward reference for pure ANSI compatibility +/// +typedef struct _EFI_SERVICE_BINDING_PROTOCOL EFI_SERVICE_BINDING_PROTOCOL; + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create + the child + @retval other The child handle was not created + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SERVICE_BINDING_CREATE_CHILD)( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Handle of the child to destroy + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle + because its services are being used. + @retval other The child handle was not destroyed + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SERVICE_BINDING_DESTROY_CHILD)( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +/// +/// The EFI_SERVICE_BINDING_PROTOCOL provides member functions to create and destroy +/// child handles. A driver is responsible for adding protocols to the child handle +/// in CreateChild() and removing protocols in DestroyChild(). It is also required +/// that the CreateChild() function opens the parent protocol BY_CHILD_CONTROLLER +/// to establish the parent-child relationship, and closes the protocol in DestroyChild(). +/// The pseudo code for CreateChild() and DestroyChild() is provided to specify the +/// required behavior, not to specify the required implementation. Each consumer of +/// a software protocol is responsible for calling CreateChild() when it requires the +/// protocol and calling DestroyChild() when it is finished with that protocol. +/// +struct _EFI_SERVICE_BINDING_PROTOCOL { + EFI_SERVICE_BINDING_CREATE_CHILD CreateChild; + EFI_SERVICE_BINDING_DESTROY_CHILD DestroyChild; +}; + +#endif Index: stand/efi/include/efidevp.h =================================================================== --- stand/efi/include/efidevp.h +++ stand/efi/include/efidevp.h @@ -232,6 +232,8 @@ UINT16 RemotePort; UINT16 Protocol; BOOLEAN StaticIpAddress; + EFI_IPv4_ADDRESS GatewayIpAddress; + EFI_IPv4_ADDRESS SubnetMask; } IPv4_DEVICE_PATH; #define MSG_IPv6_DP 0x0d @@ -294,6 +296,26 @@ UINT16 Lun; } SATA_DEVICE_PATH; + +/* DNS Device Path SubType */ +#define MSG_DNS_DP 0x1F +typedef struct { + EFI_DEVICE_PATH Header; + /* Indicates the DNS server address is IPv4 or IPv6 address. */ + UINT8 IsIPv6; + /* Instance of the DNS server address. */ + /* XXX: actually EFI_IP_ADDRESS */ + EFI_IPv4_ADDRESS DnsServerIp[]; +} DNS_DEVICE_PATH; + +/* Uniform Resource Identifiers (URI) Device Path SubType */ +#define MSG_URI_DP 0x18 +typedef struct { + EFI_DEVICE_PATH Header; + /* Instance of the URI pursuant to RFC 3986. */ + CHAR8 Uri[]; +} URI_DEVICE_PATH; + #define MEDIA_DEVICE_PATH 0x04 #define MEDIA_HARDDRIVE_DP 0x01 Index: stand/efi/include/efilib.h =================================================================== --- stand/efi/include/efilib.h +++ stand/efi/include/efilib.h @@ -42,6 +42,7 @@ extern struct devsw efipart_fddev; extern struct devsw efipart_cddev; extern struct devsw efipart_hddev; +extern struct devsw efihttp_dev; extern struct devsw efinet_dev; extern struct netif_driver efinetif; Index: stand/efi/libefi/Makefile =================================================================== --- stand/efi/libefi/Makefile +++ stand/efi/libefi/Makefile @@ -12,6 +12,7 @@ efi_driver_utils.c \ efichar.c \ efienv.c \ + efihttp.c \ efinet.c \ efipart.c \ efizfs.c \ Index: stand/efi/libefi/efihttp.c =================================================================== --- /dev/null +++ stand/efi/libefi/efihttp.c @@ -0,0 +1,766 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +/* Poll timeout in milliseconds */ +static const int EFIHTTP_POLL_TIMEOUT = 300000; + +static EFI_GUID http_guid = EFI_HTTP_PROTOCOL_GUID; +static EFI_GUID httpsb_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID; +static EFI_GUID ip4config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID; + +static int efihttp_dev_init(void); +static int efihttp_dev_strategy(void *devdata, int rw, daddr_t blk, size_t size, + char *buf, size_t *rsize); +static int efihttp_dev_open(struct open_file *f, ...); +static int efihttp_dev_close(struct open_file *f); + +static int efihttp_fs_open(const char *path, struct open_file *f); +static int efihttp_fs_close(struct open_file *f); +static int efihttp_fs_read(struct open_file *f, void *buf, size_t size, + size_t *resid); +static int efihttp_fs_write(struct open_file *f, const void *buf, size_t size, + size_t *resid); +static off_t efihttp_fs_seek(struct open_file *f, off_t offset, int where); +static int efihttp_fs_stat(struct open_file *f, struct stat *sb); +static int efihttp_fs_readdir(struct open_file *f, struct dirent *d); + +struct open_efihttp { + EFI_HTTP_PROTOCOL *http; + EFI_HANDLE http_handle; + EFI_HANDLE dev_handle; + char *uri_base; +}; + +struct file_efihttp { + ssize_t size; + off_t offset; + char *path; + bool is_dir; +}; + +struct devsw efihttp_dev = { + .dv_name = "http", + .dv_type = DEVT_NET, + .dv_init = efihttp_dev_init, + .dv_strategy = efihttp_dev_strategy, + .dv_open = efihttp_dev_open, + .dv_close = efihttp_dev_close, + .dv_ioctl = noioctl, + .dv_print = NULL, + .dv_cleanup = NULL, +}; + +struct fs_ops efihttp_fsops = { + .fs_name = "efihttp", + .fo_open = efihttp_fs_open, + .fo_close = efihttp_fs_close, + .fo_read = efihttp_fs_read, + .fo_write = efihttp_fs_write, + .fo_seek = efihttp_fs_seek, + .fo_stat = efihttp_fs_stat, + .fo_readdir = efihttp_fs_readdir, +}; + +static void EFIAPI +notify(EFI_EVENT event, void *context) +{ + bool *b; + + b = (bool *)context; + *b = true; +} + +static int +setup_ipv4_config2(EFI_HANDLE handle, MAC_ADDR_DEVICE_PATH *mac, + IPv4_DEVICE_PATH *ipv4, DNS_DEVICE_PATH *dns) +{ + EFI_IP4_CONFIG2_PROTOCOL *ip4config2; + EFI_STATUS status; + + status = BS->OpenProtocol(handle, &ip4config2_guid, + (void **)&ip4config2, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(status)) + return (efi_status_to_errno(status)); + if (ipv4) { + setenv("boot.netif.hwaddr", + ether_sprintf((u_char *)mac->MacAddress.Addr), 1); + setenv("boot.netif.ip", + inet_ntoa(*(struct in_addr *)ipv4->LocalIpAddress.Addr), 1); + setenv("boot.netif.netmask", + intoa(*(n_long *)ipv4->SubnetMask.Addr), 1); + setenv("boot.netif.gateway", + inet_ntoa(*(struct in_addr *)ipv4->GatewayIpAddress.Addr), + 1); + status = ip4config2->SetData(ip4config2, + Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY), + &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyStatic }); + if (EFI_ERROR(status)) + return (efi_status_to_errno(status)); + + status = ip4config2->SetData(ip4config2, + Ip4Config2DataTypeManualAddress, + sizeof(EFI_IP4_CONFIG2_MANUAL_ADDRESS), + &(EFI_IP4_CONFIG2_MANUAL_ADDRESS) { + .Address = ipv4->LocalIpAddress, + .SubnetMask = ipv4->SubnetMask }); + if (EFI_ERROR(status)) + return (efi_status_to_errno(status)); + + if (ipv4->GatewayIpAddress.Addr[0] != 0) { + status = ip4config2->SetData(ip4config2, + Ip4Config2DataTypeGateway, sizeof(EFI_IPv4_ADDRESS), + &ipv4->GatewayIpAddress); + if (EFI_ERROR(status)) + return (efi_status_to_errno(status)); + } + + if (dns) { + status = ip4config2->SetData(ip4config2, + Ip4Config2DataTypeDnsServer, + sizeof(EFI_IPv4_ADDRESS), &dns->DnsServerIp); + if (EFI_ERROR(status)) + return (efi_status_to_errno(status)); + } + } else { + status = ip4config2->SetData(ip4config2, + Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY), + &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyDhcp }); + if (EFI_ERROR(status)) + return (efi_status_to_errno(status)); + } + + return (0); +} + +static int +efihttp_dev_init(void) +{ + EFI_DEVICE_PATH *imgpath, *devpath; + URI_DEVICE_PATH *uri; + EFI_HANDLE handle; + EFI_STATUS status; + int err; + bool found_http; + + imgpath = efi_lookup_image_devpath(IH); + if (imgpath == NULL) + return (ENXIO); + devpath = imgpath; + found_http = false; + for (; !IsDevicePathEnd(devpath); + devpath = NextDevicePathNode(devpath)) { + if (DevicePathType(devpath) != MESSAGING_DEVICE_PATH || + DevicePathSubType(devpath) != MSG_URI_DP) + continue; + uri = (URI_DEVICE_PATH *)devpath; + if (strncmp("http", uri->Uri, 4) == 0) + found_http = true; + } + if (!found_http) + return (ENXIO); + + status = BS->LocateDevicePath(&httpsb_guid, &imgpath, &handle); + if (EFI_ERROR(status)) + return (efi_status_to_errno(status)); + + err = efi_register_handles(&efihttp_dev, &handle, NULL, 1); + return (err); +} + +static int +efihttp_dev_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, + size_t *rsize) +{ + return (EIO); +} + +static int +efihttp_dev_open(struct open_file *f, ...) +{ + EFI_HTTP_CONFIG_DATA config; + EFI_HTTPv4_ACCESS_POINT config_access; + DNS_DEVICE_PATH *dns; + EFI_DEVICE_PATH *devpath, *imgpath; + EFI_SERVICE_BINDING_PROTOCOL *sb; + IPv4_DEVICE_PATH *ipv4; + MAC_ADDR_DEVICE_PATH *mac; + URI_DEVICE_PATH *uri; + struct devdesc *dev; + struct open_efihttp *oh; + char *c; + EFI_HANDLE handle; + EFI_STATUS status; + int err, len; + + imgpath = efi_lookup_image_devpath(IH); + if (imgpath == NULL) + return (ENXIO); + devpath = imgpath; + status = BS->LocateDevicePath(&httpsb_guid, &devpath, &handle); + if (EFI_ERROR(status)) + return (efi_status_to_errno(status)); + ipv4 = NULL; + dns = NULL; + uri = NULL; + for (; !IsDevicePathEnd(imgpath); + imgpath = NextDevicePathNode(imgpath)) { + if (DevicePathType(imgpath) != MESSAGING_DEVICE_PATH) + continue; + switch (DevicePathSubType(imgpath)) { + case MSG_MAC_ADDR_DP: + mac = (MAC_ADDR_DEVICE_PATH *)imgpath; + break; + case MSG_IPv4_DP: + ipv4 = (IPv4_DEVICE_PATH *)imgpath; + break; + case MSG_DNS_DP: + dns = (DNS_DEVICE_PATH *)imgpath; + break; + case MSG_URI_DP: + uri = (URI_DEVICE_PATH *)imgpath; + break; + default: + break; + } + } + + if (uri == NULL) + return (ENXIO); + + err = setup_ipv4_config2(handle, mac, ipv4, dns); + if (err) + return (err); + + oh = calloc(1, sizeof(struct open_efihttp)); + if (!oh) + return (ENOMEM); + oh->dev_handle = handle; + dev = (struct devdesc *)f->f_devdata; + dev->d_opendata = oh; + + status = BS->OpenProtocol(handle, &httpsb_guid, (void **)&sb, IH, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(status)) { + err = efi_status_to_errno(status); + goto end; + } + + status = sb->CreateChild(sb, &oh->http_handle); + if (EFI_ERROR(status)) { + err = efi_status_to_errno(status); + goto end; + } + + status = BS->OpenProtocol(oh->http_handle, &http_guid, + (void **)&oh->http, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(status)) { + sb->DestroyChild(sb, oh->http_handle); + err = efi_status_to_errno(status); + goto end; + } + + config.HttpVersion = HttpVersion11; + config.TimeOutMillisec = 0; + config.LocalAddressIsIPv6 = FALSE; + config.AccessPoint.IPv4Node = &config_access; + config_access.UseDefaultAddress = TRUE; + config_access.LocalPort = 0; + status = oh->http->Configure(oh->http, &config); + if (EFI_ERROR(status)) { + sb->DestroyChild(sb, oh->http_handle); + err = efi_status_to_errno(status); + goto end; + } + + /* + * Here we make attempt to construct a "base" URI by stripping + * the last two path components from the loaded URI under the + * assumption that it is something like: + * + * http://127.0.0.1/foo/boot/loader.efi + * + * hoping to arriving at: + * + * http://127.0.0.1/foo/ + */ + len = DevicePathNodeLength(&uri->Header) - sizeof(URI_DEVICE_PATH); + oh->uri_base = malloc(len + 1); + if (oh->uri_base == NULL) { + err = ENOMEM; + goto end; + } + strncpy(oh->uri_base, uri->Uri, len); + oh->uri_base[len] = '\0'; + c = strrchr(oh->uri_base, '/'); + if (c != NULL) + *c = '\0'; + c = strrchr(oh->uri_base, '/'); + if (c != NULL && *(c + 1) != '\0') + *(c + 1) = '\0'; + + err = 0; +end: + if (err != 0) { + free(dev->d_opendata); + dev->d_opendata = NULL; + } + return (err); +} + +static int +efihttp_dev_close(struct open_file *f) +{ + EFI_SERVICE_BINDING_PROTOCOL *sb; + struct devdesc *dev; + struct open_efihttp *oh; + EFI_STATUS status; + + dev = (struct devdesc *)f->f_devdata; + oh = (struct open_efihttp *)dev->d_opendata; + status = BS->OpenProtocol(oh->dev_handle, &httpsb_guid, (void **)&sb, + IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(status)) + return (efi_status_to_errno(status)); + sb->DestroyChild(sb, oh->http_handle); + free(oh->uri_base); + free(oh); + dev->d_opendata = NULL; + return (0); +} + +static int +_efihttp_fs_open(const char *path, struct open_file *f) +{ + EFI_HTTP_CONFIG_DATA config; + EFI_HTTPv4_ACCESS_POINT config_access; + EFI_HTTP_TOKEN token; + EFI_HTTP_MESSAGE message; + EFI_HTTP_REQUEST_DATA request; + EFI_HTTP_RESPONSE_DATA response; + EFI_HTTP_HEADER headers[3]; + char *host, *hostp; + char *c; + struct devdesc *dev; + struct open_efihttp *oh; + struct file_efihttp *fh; + EFI_STATUS status; + int i; + int polltime; + bool done; + + dev = (struct devdesc *)f->f_devdata; + oh = (struct open_efihttp *)dev->d_opendata; + fh = calloc(1, sizeof(struct file_efihttp)); + if (fh == NULL) + return (ENOMEM); + f->f_fsdata = fh; + fh->path = strdup(path); + + /* + * Reset the HTTP state. + * + * EDK II's persistent HTTP connection handling is graceless, + * assuming that all connections are persistent regardless of + * any Connection: header or HTTP version reported by the + * server, and failing to send requests when a more sane + * implementation would seem to be just reestablishing the + * closed connection. + * + * In the hopes of having some robustness, we indicate to the + * server that we will close the connection by using a + * Connection: close header. And then here we manually + * unconfigure and reconfigure the http instance to force the + * connection closed. + */ + memset(&config, 0, sizeof(config)); + memset(&config_access, 0, sizeof(config_access)); + config.AccessPoint.IPv4Node = &config_access; + status = oh->http->GetModeData(oh->http, &config); + if (EFI_ERROR(status)) + return (efi_status_to_errno(status)); + status = oh->http->Configure(oh->http, NULL); + if (EFI_ERROR(status)) + return (efi_status_to_errno(status)); + status = oh->http->Configure(oh->http, &config); + if (EFI_ERROR(status)) + return (efi_status_to_errno(status)); + + /* Send the read request */ + done = false; + status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify, + &done, &token.Event); + if (EFI_ERROR(status)) + return (efi_status_to_errno(status)); + + /* extract the host portion of the URL */ + host = strdup(oh->uri_base); + if (host == NULL) + return (ENOMEM); + hostp = host; + /* Remove the protocol scheme */ + c = strchr(host, '/'); + if (c != NULL && *(c + 1) == '/') + hostp = (c + 2); + + /* Remove any path information */ + c = strchr(hostp, '/'); + if (c != NULL) + *c = '\0'; + + token.Status = EFI_NOT_READY; + token.Message = &message; + message.Data.Request = &request; + message.HeaderCount = 3; + message.Headers = headers; + message.BodyLength = 0; + message.Body = NULL; + request.Method = HttpMethodGet; + request.Url = calloc(strlen(oh->uri_base) + strlen(path) + 1, 2); + headers[0].FieldName = "Host"; + headers[0].FieldValue = hostp; + headers[1].FieldName = "Connection"; + headers[1].FieldValue = "close"; + headers[2].FieldName = "Accept"; + headers[2].FieldValue = "*/*"; + cpy8to16(oh->uri_base, request.Url, strlen(oh->uri_base)); + cpy8to16(path, request.Url + strlen(oh->uri_base), strlen(path)); + status = oh->http->Request(oh->http, &token); + free(request.Url); + free(host); + if (EFI_ERROR(status)) { + BS->CloseEvent(token.Event); + return (efi_status_to_errno(status)); + } + + polltime = 0; + while (!done && polltime < EFIHTTP_POLL_TIMEOUT) { + status = oh->http->Poll(oh->http); + if (EFI_ERROR(status)) + break; + + if (!done) { + delay(100 * 1000); + polltime += 100; + } + } + BS->CloseEvent(token.Event); + if (EFI_ERROR(token.Status)) + return (efi_status_to_errno(token.Status)); + + /* Wait for the read response */ + done = false; + status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify, + &done, &token.Event); + if (EFI_ERROR(status)) + return (efi_status_to_errno(status)); + token.Status = EFI_NOT_READY; + token.Message = &message; + message.Data.Response = &response; + message.HeaderCount = 0; + message.Headers = NULL; + message.BodyLength = 0; + message.Body = NULL; + response.StatusCode = HTTP_STATUS_UNSUPPORTED_STATUS; + status = oh->http->Response(oh->http, &token); + if (EFI_ERROR(status)) { + BS->CloseEvent(token.Event); + return (efi_status_to_errno(status)); + } + + polltime = 0; + while (!done && polltime < EFIHTTP_POLL_TIMEOUT) { + status = oh->http->Poll(oh->http); + if (EFI_ERROR(status)) + break; + + if (!done) { + delay(100 * 1000); + polltime += 100; + } + } + BS->CloseEvent(token.Event); + if (EFI_ERROR(token.Status)) { + BS->FreePool(message.Headers); + return (efi_status_to_errno(token.Status)); + } + if (response.StatusCode != HTTP_STATUS_200_OK) { + BS->FreePool(message.Headers); + return (EIO); + } + fh->size = 0; + fh->is_dir = false; + for (i = 0; i < message.HeaderCount; i++) { + if (strcasecmp(message.Headers[i].FieldName, + "Content-Length") == 0) + fh->size = strtoul(message.Headers[i].FieldValue, NULL, + 10); + else if (strcasecmp(message.Headers[i].FieldName, + "Content-type") == 0) { + if (strncmp(message.Headers[i].FieldValue, "text/html", + 9) == 0) + fh->is_dir = true; + } + } + + return (0); +} + +static int +efihttp_fs_open(const char *path, struct open_file *f) +{ + char *path_slash; + int err; + + /* + * If any path fails to open, try with a trailing slash in + * case it's a directory. + */ + err = _efihttp_fs_open(path, f); + if (err != 0) { + path_slash = malloc(strlen(path) + 2); + if (path_slash == NULL) + return (ENOMEM); + strcpy(path_slash, path); + strcat(path_slash, "/"); + err = _efihttp_fs_open(path_slash, f); + free(path_slash); + } + return (err); +} + +static int +efihttp_fs_close(struct open_file *f) +{ + return (0); +} + +static int +_efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid) +{ + EFI_HTTP_TOKEN token; + EFI_HTTP_MESSAGE message; + EFI_STATUS status; + struct devdesc *dev; + struct open_efihttp *oh; + struct file_efihttp *fh; + bool done; + int polltime; + + fh = (struct file_efihttp *)f->f_fsdata; + + if (fh->size > 0 && fh->offset >= fh->size) { + if (resid != NULL) + *resid = size; + + return 0; + } + + dev = (struct devdesc *)f->f_devdata; + oh = (struct open_efihttp *)dev->d_opendata; + done = false; + status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify, + &done, &token.Event); + if (EFI_ERROR(status)) { + return (efi_status_to_errno(status)); + } + token.Status = EFI_NOT_READY; + token.Message = &message; + message.Data.Request = NULL; + message.HeaderCount = 0; + message.Headers = NULL; + message.BodyLength = size; + message.Body = buf; + status = oh->http->Response(oh->http, &token); + if (status == EFI_CONNECTION_FIN) { + if (resid) + *resid = size; + return (0); + } else if (EFI_ERROR(status)) { + BS->CloseEvent(token.Event); + return (efi_status_to_errno(status)); + } + polltime = 0; + while (!done && polltime < EFIHTTP_POLL_TIMEOUT) { + status = oh->http->Poll(oh->http); + if (EFI_ERROR(status)) + break; + + if (!done) { + delay(100 * 1000); + polltime += 100; + } + } + BS->CloseEvent(token.Event); + if (token.Status == EFI_CONNECTION_FIN) { + if (resid) + *resid = size; + return (0); + } else if (EFI_ERROR(token.Status)) + return (efi_status_to_errno(token.Status)); + if (resid) + *resid = size - message.BodyLength; + fh->offset += message.BodyLength; + return (0); +} + +static int +efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid) +{ + size_t res; + int err; + + while (size > 0) { + err = _efihttp_fs_read(f, buf, size, &res); + if (err != 0 || res == size) + goto end; + buf += (size - res); + size = res; + } +end: + if (resid) + *resid = size; + return (err); +} + +static int +efihttp_fs_write(struct open_file *f, const void *buf, size_t size, size_t *resid) +{ + return (EIO); +} + +static off_t +efihttp_fs_seek(struct open_file *f, off_t offset, int where) +{ + struct file_efihttp *fh; + char *path; + void *buf; + size_t res, res2; + int err; + + fh = (struct file_efihttp *)f->f_fsdata; + if (where == SEEK_SET && fh->offset == offset) + return (0); + if (where == SEEK_SET && fh->offset < offset) { + buf = malloc(1500); + res = offset - fh->offset; + while (res > 0) { + err = _efihttp_fs_read(f, buf, min(1500, res), &res2); + if (err != 0) { + free(buf); + return (err); + } + res -= min(1500, res) - res2; + } + free(buf); + return (0); + } else if (where == SEEK_SET) { + path = fh->path; + fh->path = NULL; + efihttp_fs_close(f); + err = efihttp_fs_open(path, f); + free(path); + if (err != 0) + return (err); + return efihttp_fs_seek(f, offset, where); + } + return (EIO); +} + +static int +efihttp_fs_stat(struct open_file *f, struct stat *sb) +{ + struct file_efihttp *fh; + + fh = (struct file_efihttp *)f->f_fsdata; + memset(sb, 0, sizeof(*sb)); + sb->st_nlink = 1; + sb->st_mode = 0777 | (fh->is_dir ? S_IFDIR : S_IFREG); + sb->st_size = fh->size; + return (0); +} + +static int +efihttp_fs_readdir(struct open_file *f, struct dirent *d) +{ + static char *dirbuf = NULL, *db2, *cursor; + static int dirbuf_len = 0; + char *end; + struct file_efihttp *fh; + + fh = (struct file_efihttp *)f->f_fsdata; + if (dirbuf_len < fh->size) { + db2 = realloc(dirbuf, fh->size); + if (db2 == NULL) { + free(dirbuf); + return (ENOMEM); + } else + dirbuf = db2; + + dirbuf_len = fh->size; + } + + if (fh->offset != fh->size) { + efihttp_fs_seek(f, 0, SEEK_SET); + efihttp_fs_read(f, dirbuf, dirbuf_len, NULL); + cursor = dirbuf; + } + + cursor = strstr(cursor, "d_type = DT_DIR; + } else + d->d_type = DT_REG; + memcpy(d->d_name, cursor, end - cursor); + d->d_name[end - cursor] = '\0'; + + return (0); +} Index: stand/efi/loader/conf.c =================================================================== --- stand/efi/loader/conf.c +++ stand/efi/loader/conf.c @@ -39,6 +39,7 @@ &efipart_fddev, &efipart_cddev, &efipart_hddev, + &efihttp_dev, /* ordering with efinet_dev matters */ &efinet_dev, &vdisk_dev, #ifdef EFI_ZFS_BOOT @@ -54,6 +55,7 @@ &dosfs_fsops, &ufs_fsops, &cd9660_fsops, + &efihttp_fsops, &tftp_fsops, &nfs_fsops, &gzipfs_fsops, Index: stand/libsa/stand.h =================================================================== --- stand/libsa/stand.h +++ stand/libsa/stand.h @@ -126,6 +126,7 @@ extern struct fs_ops ext2fs_fsops; extern struct fs_ops splitfs_fsops; extern struct fs_ops pkgfs_fsops; +extern struct fs_ops efihttp_fsops; /* where values for lseek(2) */ #define SEEK_SET 0 /* set file offset to offset */