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 char *full_path
;
60 * Not in the EFI header set yet, so I have to declare it here
64 UINT32 TransactionId
:24;
65 UINT8 DhcpOptions
[1024];
66 } EFI_PXE_BASE_CODE_DHCPV6_PACKET
;
72 } EFI_DHCP6_PACKET_OPTION
;
76 * Returns TRUE if we identify a protocol that is enabled and Providing us with
77 * the needed information to fetch a grubx64.efi image
79 BOOLEAN
findNetboot(EFI_HANDLE image_handle
)
81 UINTN bs
= sizeof(EFI_HANDLE
);
82 EFI_GUID pxe_base_code_protocol
= EFI_PXE_BASE_CODE_PROTOCOL
;
85 void *buffer
= AllocatePool(bs
);
94 status
= uefi_call_wrapper(BS
->LocateHandle
,5, ByProtocol
,
95 &pxe_base_code_protocol
, NULL
, &bs
,
98 if (status
== EFI_BUFFER_TOO_SMALL
) {
103 buffer
= AllocatePool(bs
);
109 if (status
== EFI_NOT_FOUND
) {
115 * We have a list of pxe supporting protocols, lets see if any are
120 for (i
=0; i
< (bs
/ sizeof(EFI_HANDLE
)); i
++) {
121 status
= uefi_call_wrapper(BS
->OpenProtocol
, 6, hbuf
[i
],
122 &pxe_base_code_protocol
,
123 &pxe
, image_handle
, NULL
,
124 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
126 if (status
!= EFI_SUCCESS
) {
131 if (!pxe
|| !pxe
->Mode
) {
136 if (pxe
->Mode
->Started
&& pxe
->Mode
->DhcpAckReceived
) {
138 * We've located a pxe protocol handle thats been
139 * started and has received an ACK, meaning its
140 * something we'll be able to get tftp server info
153 static char *get_v6_bootfile_url(EFI_PXE_BASE_CODE_DHCPV6_PACKET
*pkt
)
156 EFI_DHCP6_PACKET_OPTION
*option
;
160 optr
= pkt
->DhcpOptions
;
163 option
= (EFI_DHCP6_PACKET_OPTION
*)optr
;
165 if (ntohs(option
->OpCode
) == 0)
168 if (ntohs(option
->OpCode
) == 59) {
169 /* This is the bootfile url option */
170 urllen
= ntohs(option
->Length
);
171 url
= AllocatePool(urllen
+2);
174 memset(url
, 0, urllen
+2);
175 memcpy(url
, option
->Data
, urllen
);
178 optr
+= 4 + ntohs(option
->Length
);
184 static UINT16
str2ns(UINT8
*str
)
189 if ('0' <= *str
&& *str
<= '9')
191 else if ('A' <= *str
&& *str
<= 'F')
193 else if ('a' <= *str
&& *str
<= 'f')
197 ret
= (ret
<< 4) + v
;
202 static UINT8
*str2ip6(char *str
)
209 for(i
=0; i
< 8; i
++) {
212 len
= strlen((UINT8
*)str
);
213 a
= b
= (UINT8
*)str
;
214 for(i
=p
=0; i
< len
; i
++, b
++) {
224 a
= b
= (UINT8
*)(str
+ len
);
225 for(j
=len
, p
=7; j
> i
; j
--, a
--) {
230 ip
[p
--] = str2ns(a
+1);
237 static BOOLEAN
extract_tftp_info(char *url
)
241 char *template = "/grubx64.efi";
243 if (strncmp((UINT8
*)url
, (UINT8
*)"tftp://", 7)) {
244 Print(L
"URLS MUST START WITH tftp://\n");
249 Print(L
"TFTP SERVER MUST BE ENCLOSED IN [..]\n");
255 while ((*end
!= '\0') && (*end
!= ']')) {
259 Print(L
"TFTP SERVER MUST BE ENCLOSED IN [..]\n");
263 memset(ip6str
, 0, 128);
264 memcpy(ip6str
, start
, strlen((UINT8
*)start
));
267 memcpy(&tftp_addr
.v6
, str2ip6(ip6str
), 16);
268 full_path
= AllocatePool(strlen((UINT8
*)end
)+strlen((UINT8
*)template)+1);
271 memset(full_path
, 0, strlen((UINT8
*)end
)+strlen((UINT8
*)template));
272 memcpy(full_path
, end
, strlen((UINT8
*)end
));
273 end
= strrchr(full_path
, '/');
276 memcpy(end
, template, strlen((UINT8
*)template));
281 static EFI_STATUS
parseDhcp6()
283 EFI_PXE_BASE_CODE_DHCPV6_PACKET
*packet
= (EFI_PXE_BASE_CODE_DHCPV6_PACKET
*)&pxe
->Mode
->DhcpAck
.Raw
;
287 bootfile_url
= get_v6_bootfile_url(packet
);
288 if (extract_tftp_info(bootfile_url
) == FALSE
)
289 return EFI_NOT_FOUND
;
291 return EFI_NOT_FOUND
;
295 static EFI_STATUS
parseDhcp4()
297 char *template = "/grubx64.efi";
298 char *tmp
= AllocatePool(16);
302 return EFI_OUT_OF_RESOURCES
;
305 memcpy(&tftp_addr
.v4
, pxe
->Mode
->DhcpAck
.Dhcpv4
.BootpSiAddr
, 4);
307 memcpy(tmp
, template, 12);
311 /* Note we don't capture the filename option here because we know its shim.efi
312 * We instead assume the filename at the end of the path is going to be grubx64.efi
317 EFI_STATUS
parseNetbootinfo(EFI_HANDLE image_handle
)
323 return EFI_NOT_READY
;
325 memset((UINT8
*)&tftp_addr
, 0, sizeof(tftp_addr
));
328 * If we've discovered an active pxe protocol figure out
329 * if its ipv4 or ipv6
331 if (pxe
->Mode
->UsingIpv6
){
338 EFI_STATUS
FetchNetbootimage(EFI_HANDLE image_handle
, VOID
**buffer
, UINTN
*bufsiz
)
341 EFI_PXE_BASE_CODE_TFTP_OPCODE read
= EFI_PXE_BASE_CODE_TFTP_READ_FILE
;
342 BOOLEAN overwrite
= FALSE
;
343 BOOLEAN nobuffer
= FALSE
;
346 Print(L
"Fetching Netboot Image\n");
347 if (*buffer
== NULL
) {
348 *buffer
= AllocatePool(4096 * 1024);
350 return EFI_OUT_OF_RESOURCES
;
351 *bufsiz
= 4096 * 1024;
355 rc
= uefi_call_wrapper(pxe
->Mtftp
, 10, pxe
, read
, *buffer
, overwrite
,
356 &bufsiz
, &blksz
, &tftp_addr
, full_path
, NULL
, nobuffer
);
358 if (rc
== EFI_BUFFER_TOO_SMALL
) {
359 /* try again, doubling buf size */
362 *buffer
= AllocatePool(*bufsiz
);
364 return EFI_OUT_OF_RESOURCES
;