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 #define ntohs(x) __builtin_bswap16(x) /* supported both by GCC and clang */
44 #define htons(x) ntohs(x)
46 static EFI_PXE_BASE_CODE
*pxe
;
47 static EFI_IP_ADDRESS tftp_addr
;
48 static CHAR8
*full_path
;
55 } EFI_DHCP6_PACKET_OPTION
;
58 translate_slashes(char *str
)
65 for (i
= 0, j
= 0; str
[i
] != '\0'; i
++, j
++) {
77 * Returns TRUE if we identify a protocol that is enabled and Providing us with
78 * the needed information to fetch a grubx64.efi image
80 BOOLEAN
findNetboot(EFI_HANDLE device
)
84 status
= uefi_call_wrapper(BS
->HandleProtocol
, 3, device
,
85 &PxeBaseCodeProtocol
, (VOID
**)&pxe
);
86 if (status
!= EFI_SUCCESS
) {
91 if (!pxe
|| !pxe
->Mode
) {
96 if (!pxe
->Mode
->Started
|| !pxe
->Mode
->DhcpAckReceived
) {
102 * We've located a pxe protocol handle thats been started and has
103 * received an ACK, meaning its something we'll be able to get
104 * tftp server info out of
109 static CHAR8
*get_v6_bootfile_url(EFI_PXE_BASE_CODE_DHCPV6_PACKET
*pkt
)
112 EFI_DHCP6_PACKET_OPTION
*option
;
116 optr
= pkt
->DhcpOptions
;
119 option
= (EFI_DHCP6_PACKET_OPTION
*)optr
;
121 if (ntohs(option
->OpCode
) == 0)
124 if (ntohs(option
->OpCode
) == 59) {
125 /* This is the bootfile url option */
126 urllen
= ntohs(option
->Length
);
127 url
= AllocateZeroPool(urllen
+1);
130 memcpy(url
, option
->Data
, urllen
);
133 optr
+= 4 + ntohs(option
->Length
);
139 static CHAR16
str2ns(CHAR8
*str
)
144 if ('0' <= *str
&& *str
<= '9')
146 else if ('A' <= *str
&& *str
<= 'F')
148 else if ('a' <= *str
&& *str
<= 'f')
152 ret
= (ret
<< 4) + v
;
157 static CHAR8
*str2ip6(CHAR8
*str
)
164 for(i
=0; i
< 8; i
++) {
169 for(i
=p
=0; i
< len
; i
++, b
++) {
180 for(j
=len
, p
=7; j
> i
; j
--, a
--) {
185 ip
[p
--] = str2ns(a
+1);
192 static BOOLEAN
extract_tftp_info(CHAR8
*url
)
196 CHAR8
*template = (CHAR8
*)translate_slashes(DEFAULT_LOADER_CHAR
);
198 if (strncmp((UINT8
*)url
, (UINT8
*)"tftp://", 7)) {
199 Print(L
"URLS MUST START WITH tftp://\n");
204 Print(L
"TFTP SERVER MUST BE ENCLOSED IN [..]\n");
210 while ((*end
!= '\0') && (*end
!= ']')) {
212 if (end
- start
> 39) {
213 Print(L
"TFTP URL includes malformed IPv6 address\n");
218 Print(L
"TFTP SERVER MUST BE ENCLOSED IN [..]\n");
221 memset(ip6str
, 0, 40);
222 memcpy(ip6str
, start
, end
- start
);
224 memcpy(&tftp_addr
.v6
, str2ip6(ip6str
), 16);
225 full_path
= AllocateZeroPool(strlen(end
)+strlen(template)+1);
228 memcpy(full_path
, end
, strlen(end
));
229 end
= (CHAR8
*)strrchr((char *)full_path
, '/');
231 end
= (CHAR8
*)full_path
;
232 memcpy(end
, template, strlen(template));
233 end
[strlen(template)] = '\0';
238 static EFI_STATUS
parseDhcp6()
240 EFI_PXE_BASE_CODE_DHCPV6_PACKET
*packet
= (EFI_PXE_BASE_CODE_DHCPV6_PACKET
*)&pxe
->Mode
->DhcpAck
.Raw
;
243 bootfile_url
= get_v6_bootfile_url(packet
);
245 return EFI_NOT_FOUND
;
246 if (extract_tftp_info(bootfile_url
) == FALSE
) {
247 FreePool(bootfile_url
);
248 return EFI_NOT_FOUND
;
250 FreePool(bootfile_url
);
254 static EFI_STATUS
parseDhcp4()
256 CHAR8
*template = (CHAR8
*)translate_slashes(DEFAULT_LOADER_CHAR
);
257 INTN template_len
= strlen(template) + 1;
259 INTN dir_len
= strnlena(pxe
->Mode
->DhcpAck
.Dhcpv4
.BootpBootFile
, 127);
261 UINT8
*dir
= pxe
->Mode
->DhcpAck
.Dhcpv4
.BootpBootFile
;
263 for (i
= dir_len
; i
>= 0; i
--) {
267 dir_len
= (i
>= 0) ? i
+ 1 : 0;
269 full_path
= AllocateZeroPool(dir_len
+ template_len
);
272 return EFI_OUT_OF_RESOURCES
;
275 strncpya(full_path
, dir
, dir_len
);
276 if (full_path
[dir_len
-1] == '/' && template[0] == '/')
277 full_path
[dir_len
-1] = '\0';
279 if (dir_len
== 0 && dir
[0] != '/' && template[0] == '/')
281 strcata(full_path
, template);
282 memcpy(&tftp_addr
.v4
, pxe
->Mode
->DhcpAck
.Dhcpv4
.BootpSiAddr
, 4);
287 EFI_STATUS
parseNetbootinfo(EFI_HANDLE image_handle
)
293 return EFI_NOT_READY
;
295 memset((UINT8
*)&tftp_addr
, 0, sizeof(tftp_addr
));
298 * If we've discovered an active pxe protocol figure out
299 * if its ipv4 or ipv6
301 if (pxe
->Mode
->UsingIpv6
){
308 EFI_STATUS
FetchNetbootimage(EFI_HANDLE image_handle
, VOID
**buffer
, UINT64
*bufsiz
)
311 EFI_PXE_BASE_CODE_TFTP_OPCODE read
= EFI_PXE_BASE_CODE_TFTP_READ_FILE
;
312 BOOLEAN overwrite
= FALSE
;
313 BOOLEAN nobuffer
= FALSE
;
316 Print(L
"Fetching Netboot Image\n");
317 if (*buffer
== NULL
) {
318 *buffer
= AllocatePool(4096 * 1024);
320 return EFI_OUT_OF_RESOURCES
;
321 *bufsiz
= 4096 * 1024;
325 rc
= uefi_call_wrapper(pxe
->Mtftp
, 10, pxe
, read
, *buffer
, overwrite
,
326 bufsiz
, &blksz
, &tftp_addr
, full_path
, NULL
, nobuffer
);
328 if (rc
== EFI_BUFFER_TOO_SMALL
) {
329 /* try again, doubling buf size */
332 *buffer
= AllocatePool(*bufsiz
);
334 return EFI_OUT_OF_RESOURCES
;
338 if (rc
!= EFI_SUCCESS
&& *buffer
) {