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
42 static inline unsigned short int __swap16(unsigned short int x
)
44 __asm__("xchgb %b0,%h0"
50 #define ntohs(x) __swap16(x)
51 #define htons(x) ntohs(x)
53 static EFI_PXE_BASE_CODE
*pxe
;
54 static EFI_IP_ADDRESS tftp_addr
;
55 static CHAR8
*full_path
;
62 } EFI_DHCP6_PACKET_OPTION
;
65 translate_slashes(char *str
)
72 for (i
= 0, j
= 0; str
[i
] != '\0'; i
++, j
++) {
84 * Returns TRUE if we identify a protocol that is enabled and Providing us with
85 * the needed information to fetch a grubx64.efi image
87 BOOLEAN
findNetboot(EFI_HANDLE image_handle
)
89 UINTN bs
= sizeof(EFI_HANDLE
);
90 EFI_GUID pxe_base_code_protocol
= EFI_PXE_BASE_CODE_PROTOCOL
;
93 void *buffer
= AllocatePool(bs
);
102 status
= uefi_call_wrapper(BS
->LocateHandle
,5, ByProtocol
,
103 &pxe_base_code_protocol
, NULL
, &bs
,
106 if (status
== EFI_BUFFER_TOO_SMALL
) {
111 buffer
= AllocatePool(bs
);
117 if (status
== EFI_NOT_FOUND
) {
123 * We have a list of pxe supporting protocols, lets see if any are
128 for (i
=0; i
< (bs
/ sizeof(EFI_HANDLE
)); i
++) {
129 status
= uefi_call_wrapper(BS
->OpenProtocol
, 6, hbuf
[i
],
130 &pxe_base_code_protocol
,
131 (void **)&pxe
, image_handle
, NULL
,
132 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
134 if (status
!= EFI_SUCCESS
) {
139 if (!pxe
|| !pxe
->Mode
) {
144 if (pxe
->Mode
->Started
&& pxe
->Mode
->DhcpAckReceived
) {
146 * We've located a pxe protocol handle thats been
147 * started and has received an ACK, meaning its
148 * something we'll be able to get tftp server info
161 static CHAR8
*get_v6_bootfile_url(EFI_PXE_BASE_CODE_DHCPV6_PACKET
*pkt
)
164 EFI_DHCP6_PACKET_OPTION
*option
;
168 optr
= pkt
->DhcpOptions
;
171 option
= (EFI_DHCP6_PACKET_OPTION
*)optr
;
173 if (ntohs(option
->OpCode
) == 0)
176 if (ntohs(option
->OpCode
) == 59) {
177 /* This is the bootfile url option */
178 urllen
= ntohs(option
->Length
);
179 url
= AllocateZeroPool(urllen
+1);
182 memcpy(url
, option
->Data
, urllen
);
185 optr
+= 4 + ntohs(option
->Length
);
191 static CHAR16
str2ns(CHAR8
*str
)
196 if ('0' <= *str
&& *str
<= '9')
198 else if ('A' <= *str
&& *str
<= 'F')
200 else if ('a' <= *str
&& *str
<= 'f')
204 ret
= (ret
<< 4) + v
;
209 static CHAR8
*str2ip6(CHAR8
*str
)
216 for(i
=0; i
< 8; i
++) {
221 for(i
=p
=0; i
< len
; i
++, b
++) {
232 for(j
=len
, p
=7; j
> i
; j
--, a
--) {
237 ip
[p
--] = str2ns(a
+1);
244 static BOOLEAN
extract_tftp_info(CHAR8
*url
)
248 CHAR8
*template = (CHAR8
*)translate_slashes(DEFAULT_LOADER_CHAR
);
250 if (strncmp((UINT8
*)url
, (UINT8
*)"tftp://", 7)) {
251 Print(L
"URLS MUST START WITH tftp://\n");
256 Print(L
"TFTP SERVER MUST BE ENCLOSED IN [..]\n");
262 while ((*end
!= '\0') && (*end
!= ']')) {
264 if (end
- start
> 39) {
265 Print(L
"TFTP URL includes malformed IPv6 address\n");
270 Print(L
"TFTP SERVER MUST BE ENCLOSED IN [..]\n");
273 memset(ip6str
, 0, 40);
274 memcpy(ip6str
, start
, end
- start
);
276 memcpy(&tftp_addr
.v6
, str2ip6(ip6str
), 16);
277 full_path
= AllocateZeroPool(strlen(end
)+strlen(template)+1);
280 memcpy(full_path
, end
, strlen(end
));
281 end
= (CHAR8
*)strrchr((char *)full_path
, '/');
283 end
= (CHAR8
*)full_path
;
284 memcpy(end
, template, strlen(template));
285 end
[strlen(template)] = '\0';
290 static EFI_STATUS
parseDhcp6()
292 EFI_PXE_BASE_CODE_DHCPV6_PACKET
*packet
= (EFI_PXE_BASE_CODE_DHCPV6_PACKET
*)&pxe
->Mode
->DhcpAck
.Raw
;
295 bootfile_url
= get_v6_bootfile_url(packet
);
297 return EFI_NOT_FOUND
;
298 if (extract_tftp_info(bootfile_url
) == FALSE
) {
299 FreePool(bootfile_url
);
300 return EFI_NOT_FOUND
;
302 FreePool(bootfile_url
);
306 static EFI_STATUS
parseDhcp4()
308 CHAR8
*template = (CHAR8
*)DEFAULT_LOADER_CHAR
;
309 full_path
= AllocateZeroPool(strlen(template)+1);
312 return EFI_OUT_OF_RESOURCES
;
314 memcpy(&tftp_addr
.v4
, pxe
->Mode
->DhcpAck
.Dhcpv4
.BootpSiAddr
, 4);
316 memcpy(full_path
, template, strlen(template));
318 /* Note we don't capture the filename option here because we know its shim.efi
319 * We instead assume the filename at the end of the path is going to be grubx64.efi
324 EFI_STATUS
parseNetbootinfo(EFI_HANDLE image_handle
)
330 return EFI_NOT_READY
;
332 memset((UINT8
*)&tftp_addr
, 0, sizeof(tftp_addr
));
335 * If we've discovered an active pxe protocol figure out
336 * if its ipv4 or ipv6
338 if (pxe
->Mode
->UsingIpv6
){
345 EFI_STATUS
FetchNetbootimage(EFI_HANDLE image_handle
, VOID
**buffer
, UINT64
*bufsiz
)
348 EFI_PXE_BASE_CODE_TFTP_OPCODE read
= EFI_PXE_BASE_CODE_TFTP_READ_FILE
;
349 BOOLEAN overwrite
= FALSE
;
350 BOOLEAN nobuffer
= FALSE
;
353 Print(L
"Fetching Netboot Image\n");
354 if (*buffer
== NULL
) {
355 *buffer
= AllocatePool(4096 * 1024);
357 return EFI_OUT_OF_RESOURCES
;
358 *bufsiz
= 4096 * 1024;
362 rc
= uefi_call_wrapper(pxe
->Mtftp
, 10, pxe
, read
, *buffer
, overwrite
,
363 bufsiz
, &blksz
, &tftp_addr
, full_path
, NULL
, nobuffer
);
365 if (rc
== EFI_BUFFER_TOO_SMALL
) {
366 /* try again, doubling buf size */
369 *buffer
= AllocatePool(*bufsiz
);
371 return EFI_OUT_OF_RESOURCES
;
375 if (rc
!= EFI_SUCCESS
&& *buffer
) {