2 * netboot - trivial UEFI first-stage bootloader netboot support
4 * Copyright 2012 Red Hat, Inc <mjg@redhat.com>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 * Significant portions of this code are derived from Tianocore
32 * (http://tianocore.sf.net) and are Copyright 2009-2012 Intel
43 static inline unsigned short int __swap16(unsigned short int x
)
45 __asm__("xchgb %b0,%h0"
51 #define ntohs(x) __swap16(x)
52 #define htons(x) ntohs(x)
54 static EFI_PXE_BASE_CODE
*pxe
;
55 static EFI_IP_ADDRESS tftp_addr
;
56 static UINT8
*full_path
;
63 } EFI_DHCP6_PACKET_OPTION
;
67 * Returns TRUE if we identify a protocol that is enabled and Providing us with
68 * the needed information to fetch a grubx64.efi image
70 BOOLEAN
findNetboot(EFI_HANDLE image_handle
)
72 UINTN bs
= sizeof(EFI_HANDLE
);
73 EFI_GUID pxe_base_code_protocol
= EFI_PXE_BASE_CODE_PROTOCOL
;
76 void *buffer
= AllocatePool(bs
);
85 status
= uefi_call_wrapper(BS
->LocateHandle
,5, ByProtocol
,
86 &pxe_base_code_protocol
, NULL
, &bs
,
89 if (status
== EFI_BUFFER_TOO_SMALL
) {
94 buffer
= AllocatePool(bs
);
100 if (status
== EFI_NOT_FOUND
) {
106 * We have a list of pxe supporting protocols, lets see if any are
111 for (i
=0; i
< (bs
/ sizeof(EFI_HANDLE
)); i
++) {
112 status
= uefi_call_wrapper(BS
->OpenProtocol
, 6, hbuf
[i
],
113 &pxe_base_code_protocol
,
114 (void **)&pxe
, image_handle
, NULL
,
115 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
117 if (status
!= EFI_SUCCESS
) {
122 if (!pxe
|| !pxe
->Mode
) {
127 if (pxe
->Mode
->Started
&& pxe
->Mode
->DhcpAckReceived
) {
129 * We've located a pxe protocol handle thats been
130 * started and has received an ACK, meaning its
131 * something we'll be able to get tftp server info
144 static char *get_v6_bootfile_url(EFI_PXE_BASE_CODE_DHCPV6_PACKET
*pkt
)
147 EFI_DHCP6_PACKET_OPTION
*option
;
151 optr
= pkt
->DhcpOptions
;
154 option
= (EFI_DHCP6_PACKET_OPTION
*)optr
;
156 if (ntohs(option
->OpCode
) == 0)
159 if (ntohs(option
->OpCode
) == 59) {
160 /* This is the bootfile url option */
161 urllen
= ntohs(option
->Length
);
162 url
= AllocatePool(urllen
+2);
165 memset(url
, 0, urllen
+2);
166 memcpy(url
, option
->Data
, urllen
);
169 optr
+= 4 + ntohs(option
->Length
);
175 static UINT16
str2ns(UINT8
*str
)
180 if ('0' <= *str
&& *str
<= '9')
182 else if ('A' <= *str
&& *str
<= 'F')
184 else if ('a' <= *str
&& *str
<= 'f')
188 ret
= (ret
<< 4) + v
;
193 static UINT8
*str2ip6(char *str
)
200 for(i
=0; i
< 8; i
++) {
203 len
= strlen((UINT8
*)str
);
204 a
= b
= (UINT8
*)str
;
205 for(i
=p
=0; i
< len
; i
++, b
++) {
215 a
= b
= (UINT8
*)(str
+ len
);
216 for(j
=len
, p
=7; j
> i
; j
--, a
--) {
221 ip
[p
--] = str2ns(a
+1);
228 static BOOLEAN
extract_tftp_info(char *url
)
232 CHAR8
*template = (CHAR8
*)"/grubx64.efi";
234 if (strncmp((UINT8
*)url
, (UINT8
*)"tftp://", 7)) {
235 Print(L
"URLS MUST START WITH tftp://\n");
238 start
= (CHAR8
*)url
+ 7;
240 Print(L
"TFTP SERVER MUST BE ENCLOSED IN [..]\n");
246 while ((*end
!= '\0') && (*end
!= ']')) {
250 Print(L
"TFTP SERVER MUST BE ENCLOSED IN [..]\n");
253 memset(ip6str
, 0, 128);
254 memcpy(ip6str
, start
, end
- start
);
256 memcpy(&tftp_addr
.v6
, str2ip6(ip6str
), 16);
257 full_path
= AllocateZeroPool(strlen(end
)+strlen(template)+1);
260 memcpy(full_path
, end
, strlen(end
));
261 end
= (CHAR8
*)strrchr((char *)full_path
, '/');
263 end
= (CHAR8
*)full_path
;
264 memcpy(end
, template, strlen(template));
265 end
[strlen(template)] = '\0';
270 static EFI_STATUS
parseDhcp6()
272 EFI_PXE_BASE_CODE_DHCPV6_PACKET
*packet
= (EFI_PXE_BASE_CODE_DHCPV6_PACKET
*)&pxe
->Mode
->DhcpAck
.Raw
;
276 bootfile_url
= get_v6_bootfile_url(packet
);
277 if (extract_tftp_info(bootfile_url
) == FALSE
)
278 return EFI_NOT_FOUND
;
280 return EFI_NOT_FOUND
;
284 static EFI_STATUS
parseDhcp4()
286 CHAR8
*template = (CHAR8
*)"/grubx64.efi";
287 full_path
= AllocateZeroPool(strlen(template)+1);
290 return EFI_OUT_OF_RESOURCES
;
292 memcpy(&tftp_addr
.v4
, pxe
->Mode
->DhcpAck
.Dhcpv4
.BootpSiAddr
, 4);
294 memcpy(full_path
, template, strlen(template));
296 /* Note we don't capture the filename option here because we know its shim.efi
297 * We instead assume the filename at the end of the path is going to be grubx64.efi
302 EFI_STATUS
parseNetbootinfo(EFI_HANDLE image_handle
)
308 return EFI_NOT_READY
;
310 memset((UINT8
*)&tftp_addr
, 0, sizeof(tftp_addr
));
313 * If we've discovered an active pxe protocol figure out
314 * if its ipv4 or ipv6
316 if (pxe
->Mode
->UsingIpv6
){
323 EFI_STATUS
FetchNetbootimage(EFI_HANDLE image_handle
, VOID
**buffer
, UINT64
*bufsiz
)
326 EFI_PXE_BASE_CODE_TFTP_OPCODE read
= EFI_PXE_BASE_CODE_TFTP_READ_FILE
;
327 BOOLEAN overwrite
= FALSE
;
328 BOOLEAN nobuffer
= FALSE
;
331 Print(L
"Fetching Netboot Image\n");
332 if (*buffer
== NULL
) {
333 *buffer
= AllocatePool(4096 * 1024);
335 return EFI_OUT_OF_RESOURCES
;
336 *bufsiz
= 4096 * 1024;
340 rc
= uefi_call_wrapper(pxe
->Mtftp
, 10, pxe
, read
, *buffer
, overwrite
,
341 bufsiz
, &blksz
, &tftp_addr
, full_path
, NULL
, nobuffer
);
343 if (rc
== EFI_BUFFER_TOO_SMALL
) {
344 /* try again, doubling buf size */
347 *buffer
= AllocatePool(*bufsiz
);
349 return EFI_OUT_OF_RESOURCES
;