1 /* $OpenBSD: efipxe.c,v 1.7 2021/03/11 11:16:56 jsg Exp $ */ 2 /* 3 * Copyright (c) 2017 Patrick Wildt <patrick@blueri.se> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/disklabel.h> 20 #include <sys/socket.h> 21 22 #include <net/if.h> 23 24 #include <netinet/in.h> 25 #include <netinet/if_ether.h> 26 #include <netinet/ip.h> 27 #include <netinet/ip_var.h> 28 #include <netinet/udp.h> 29 #include <netinet/udp_var.h> 30 31 #include <libsa.h> 32 #include <lib/libsa/tftp.h> 33 #include <lib/libsa/net.h> 34 #include <lib/libsa/netif.h> 35 36 #include <efi.h> 37 #include <efiapi.h> 38 #include "eficall.h" 39 #include "efiboot.h" 40 #include "disk.h" 41 42 extern EFI_BOOT_SERVICES *BS; 43 extern EFI_DEVICE_PATH *efi_bootdp; 44 45 extern char *bootmac; 46 static UINT8 boothw[16]; 47 struct in_addr bootip, servip; 48 extern struct in_addr gateip; 49 static EFI_GUID devp_guid = DEVICE_PATH_PROTOCOL; 50 static EFI_GUID net_guid = EFI_SIMPLE_NETWORK_PROTOCOL; 51 static EFI_GUID pxe_guid = EFI_PXE_BASE_CODE_PROTOCOL; 52 static EFI_SIMPLE_NETWORK *NET = NULL; 53 static EFI_PXE_BASE_CODE *PXE = NULL; 54 static EFI_PHYSICAL_ADDRESS txbuf; 55 static int use_mtftp = 0; 56 57 extern int efi_device_path_depth(EFI_DEVICE_PATH *dp, int); 58 extern int efi_device_path_ncmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int); 59 60 int efinet_probe(struct netif *, void *); 61 int efinet_match(struct netif *, void *); 62 void efinet_init(struct iodesc *, void *); 63 int efinet_get(struct iodesc *, void *, size_t, time_t); 64 int efinet_put(struct iodesc *, void *, size_t); 65 void efinet_end(struct netif *); 66 67 /* 68 * TFTP initial probe. This function discovers PXE handles and tries 69 * to figure out if there has already been a successful PXE handshake. 70 * If so, set the PXE variable. 71 */ 72 void 73 efi_pxeprobe(void) 74 { 75 EFI_SIMPLE_NETWORK *net; 76 EFI_PXE_BASE_CODE *pxe; 77 EFI_DEVICE_PATH *dp0; 78 EFI_HANDLE *handles; 79 EFI_STATUS status; 80 UINTN nhandles; 81 int i, depth; 82 83 if (efi_bootdp == NULL) 84 return; 85 86 status = EFI_CALL(BS->LocateHandleBuffer, ByProtocol, &pxe_guid, NULL, 87 &nhandles, &handles); 88 if (status != EFI_SUCCESS) 89 return; 90 91 for (i = 0; i < nhandles; i++) { 92 EFI_PXE_BASE_CODE_DHCPV4_PACKET *dhcp; 93 94 status = EFI_CALL(BS->HandleProtocol, handles[i], 95 &devp_guid, (void **)&dp0); 96 if (status != EFI_SUCCESS) 97 continue; 98 99 depth = efi_device_path_depth(efi_bootdp, MESSAGING_DEVICE_PATH); 100 if (depth == -1 || efi_device_path_ncmp(efi_bootdp, dp0, depth)) 101 continue; 102 103 status = EFI_CALL(BS->HandleProtocol, handles[i], &net_guid, 104 (void **)&net); 105 if (status != EFI_SUCCESS) 106 continue; 107 108 status = EFI_CALL(BS->HandleProtocol, handles[i], &pxe_guid, 109 (void **)&pxe); 110 if (status != EFI_SUCCESS) 111 continue; 112 113 if (pxe->Mode == NULL) 114 continue; 115 116 if (pxe->Mtftp != NULL) { 117 status = EFI_CALL(pxe->Mtftp, NULL, 0, NULL, 118 FALSE, NULL, NULL, NULL, NULL, NULL, FALSE); 119 if (status != EFI_UNSUPPORTED) 120 use_mtftp = 1; 121 } 122 123 dhcp = (EFI_PXE_BASE_CODE_DHCPV4_PACKET *)&pxe->Mode->DhcpAck; 124 memcpy(&bootip, dhcp->BootpYiAddr, sizeof(bootip)); 125 memcpy(&servip, dhcp->BootpSiAddr, sizeof(servip)); 126 memcpy(&gateip, dhcp->BootpSiAddr, sizeof(gateip)); 127 memcpy(boothw, dhcp->BootpHwAddr, sizeof(boothw)); 128 bootmac = boothw; 129 NET = net; 130 PXE = pxe; 131 break; 132 } 133 } 134 135 /* 136 * TFTP filesystem layer implementation. 137 */ 138 struct mtftp_handle { 139 unsigned char *inbuf; /* input buffer */ 140 size_t inbufsize; 141 off_t inbufoff; 142 }; 143 144 int 145 mtftp_open(char *path, struct open_file *f) 146 { 147 struct mtftp_handle *tftpfile; 148 EFI_PHYSICAL_ADDRESS addr; 149 EFI_IP_ADDRESS dstip; 150 EFI_STATUS status; 151 UINT64 size; 152 153 if (strcmp("tftp", f->f_dev->dv_name) != 0) 154 return ENXIO; 155 156 if (PXE == NULL) 157 return ENXIO; 158 159 if (!use_mtftp) 160 return ENXIO; 161 162 tftpfile = alloc(sizeof(*tftpfile)); 163 if (tftpfile == NULL) 164 return ENOMEM; 165 memset(tftpfile, 0, sizeof(*tftpfile)); 166 167 memcpy(&dstip, &servip, sizeof(servip)); 168 status = EFI_CALL(PXE->Mtftp, PXE, EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, 169 NULL, FALSE, &size, NULL, &dstip, path, NULL, FALSE); 170 if (status != EFI_SUCCESS) { 171 free(tftpfile, sizeof(*tftpfile)); 172 return ENOENT; 173 } 174 tftpfile->inbufsize = size; 175 176 if (tftpfile->inbufsize == 0) 177 goto out; 178 179 status = EFI_CALL(BS->AllocatePages, AllocateAnyPages, EfiLoaderData, 180 EFI_SIZE_TO_PAGES(tftpfile->inbufsize), &addr); 181 if (status != EFI_SUCCESS) { 182 free(tftpfile, sizeof(*tftpfile)); 183 return ENOMEM; 184 } 185 tftpfile->inbuf = (unsigned char *)((paddr_t)addr); 186 187 status = EFI_CALL(PXE->Mtftp, PXE, EFI_PXE_BASE_CODE_TFTP_READ_FILE, 188 tftpfile->inbuf, FALSE, &size, NULL, &dstip, path, NULL, FALSE); 189 if (status != EFI_SUCCESS) { 190 free(tftpfile, sizeof(*tftpfile)); 191 return ENXIO; 192 } 193 out: 194 f->f_fsdata = tftpfile; 195 return 0; 196 } 197 198 int 199 mtftp_close(struct open_file *f) 200 { 201 struct mtftp_handle *tftpfile = f->f_fsdata; 202 203 if (tftpfile->inbuf != NULL) 204 EFI_CALL(BS->FreePages, (paddr_t)tftpfile->inbuf, 205 EFI_SIZE_TO_PAGES(tftpfile->inbufsize)); 206 free(tftpfile, sizeof(*tftpfile)); 207 return 0; 208 } 209 210 int 211 mtftp_read(struct open_file *f, void *addr, size_t size, size_t *resid) 212 { 213 struct mtftp_handle *tftpfile = f->f_fsdata; 214 size_t toread; 215 216 if (size > tftpfile->inbufsize - tftpfile->inbufoff) 217 toread = tftpfile->inbufsize - tftpfile->inbufoff; 218 else 219 toread = size; 220 221 if (toread != 0) { 222 memcpy(addr, tftpfile->inbuf + tftpfile->inbufoff, toread); 223 tftpfile->inbufoff += toread; 224 } 225 226 if (resid != NULL) 227 *resid = size - toread; 228 return 0; 229 } 230 231 int 232 mtftp_write(struct open_file *f, void *start, size_t size, size_t *resid) 233 { 234 return EROFS; 235 } 236 237 off_t 238 mtftp_seek(struct open_file *f, off_t offset, int where) 239 { 240 struct mtftp_handle *tftpfile = f->f_fsdata; 241 242 switch(where) { 243 case SEEK_CUR: 244 if (tftpfile->inbufoff + offset < 0 || 245 tftpfile->inbufoff + offset > tftpfile->inbufsize) { 246 errno = EOFFSET; 247 break; 248 } 249 tftpfile->inbufoff += offset; 250 return (tftpfile->inbufoff); 251 case SEEK_SET: 252 if (offset < 0 || offset > tftpfile->inbufsize) { 253 errno = EOFFSET; 254 break; 255 } 256 tftpfile->inbufoff = offset; 257 return (tftpfile->inbufoff); 258 case SEEK_END: 259 tftpfile->inbufoff = tftpfile->inbufsize; 260 return (tftpfile->inbufoff); 261 default: 262 errno = EINVAL; 263 } 264 return((off_t)-1); 265 } 266 267 int 268 mtftp_stat(struct open_file *f, struct stat *sb) 269 { 270 struct mtftp_handle *tftpfile = f->f_fsdata; 271 272 sb->st_mode = 0444; 273 sb->st_nlink = 1; 274 sb->st_uid = 0; 275 sb->st_gid = 0; 276 sb->st_size = tftpfile->inbufsize; 277 278 return 0; 279 } 280 281 int 282 mtftp_readdir(struct open_file *f, char *name) 283 { 284 return EOPNOTSUPP; 285 } 286 287 /* 288 * Overload generic TFTP implementation to check that 289 * we actually have a driver. 290 */ 291 int 292 efitftp_open(char *path, struct open_file *f) 293 { 294 if (strcmp("tftp", f->f_dev->dv_name) != 0) 295 return ENXIO; 296 297 if (NET == NULL || PXE == NULL) 298 return ENXIO; 299 300 if (use_mtftp) 301 return ENXIO; 302 303 return tftp_open(path, f); 304 } 305 306 /* 307 * Dummy network device. 308 */ 309 int tftpdev_sock = -1; 310 311 int 312 tftpopen(struct open_file *f, ...) 313 { 314 EFI_STATUS status; 315 u_int unit, part; 316 va_list ap; 317 318 va_start(ap, f); 319 unit = va_arg(ap, u_int); 320 part = va_arg(ap, u_int); 321 va_end(ap); 322 323 /* No PXE set -> no PXE available */ 324 if (PXE == NULL) 325 return 1; 326 327 if (unit != 0) 328 return 1; 329 330 if (!use_mtftp) { 331 status = EFI_CALL(BS->AllocatePages, AllocateAnyPages, 332 EfiLoaderData, EFI_SIZE_TO_PAGES(RECV_SIZE), &txbuf); 333 if (status != EFI_SUCCESS) 334 return ENOMEM; 335 336 if ((tftpdev_sock = netif_open("efinet")) < 0) { 337 EFI_CALL(BS->FreePages, txbuf, 338 EFI_SIZE_TO_PAGES(RECV_SIZE)); 339 return ENXIO; 340 } 341 342 f->f_devdata = &tftpdev_sock; 343 } 344 345 return 0; 346 } 347 348 int 349 tftpclose(struct open_file *f) 350 { 351 int ret = 0; 352 353 if (!use_mtftp) { 354 ret = netif_close(*(int *)f->f_devdata); 355 EFI_CALL(BS->FreePages, txbuf, EFI_SIZE_TO_PAGES(RECV_SIZE)); 356 txbuf = 0; 357 } 358 359 return ret; 360 } 361 362 int 363 tftpioctl(struct open_file *f, u_long cmd, void *data) 364 { 365 return EOPNOTSUPP; 366 } 367 368 int 369 tftpstrategy(void *devdata, int rw, daddr_t blk, size_t size, void *buf, 370 size_t *rsize) 371 { 372 return EOPNOTSUPP; 373 } 374 375 /* 376 * Simple Network Protocol driver. 377 */ 378 struct netif_stats efinet_stats; 379 struct netif_dif efinet_ifs[] = { 380 { 0, 1, &efinet_stats, 0, 0, }, 381 }; 382 383 struct netif_driver efinet_driver = { 384 "efinet", 385 efinet_match, 386 efinet_probe, 387 efinet_init, 388 efinet_get, 389 efinet_put, 390 efinet_end, 391 efinet_ifs, 392 nitems(efinet_ifs) 393 }; 394 395 int 396 efinet_match(struct netif *nif, void *v) 397 { 398 return 1; 399 } 400 401 int 402 efinet_probe(struct netif *nif, void *v) 403 { 404 if (strncmp(v, efinet_driver.netif_bname, 3)) 405 return -1; 406 407 return 0; 408 } 409 410 void 411 efinet_init(struct iodesc *desc, void *v) 412 { 413 EFI_SIMPLE_NETWORK *net = NET; 414 EFI_STATUS status; 415 416 if (net == NULL) 417 return; 418 419 if (net->Mode->State == EfiSimpleNetworkStopped) { 420 status = EFI_CALL(net->Start, net); 421 if (status != EFI_SUCCESS) 422 return; 423 } 424 425 if (net->Mode->State != EfiSimpleNetworkInitialized) { 426 status = EFI_CALL(net->Initialize, net, 0, 0); 427 if (status != EFI_SUCCESS) 428 return; 429 } 430 431 EFI_CALL(net->ReceiveFilters, net, 432 EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | 433 EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST, 434 0, FALSE, 0, NULL); 435 436 memcpy(desc->myea, net->Mode->CurrentAddress.Addr, 6); 437 memcpy(&desc->myip, &bootip, sizeof(bootip)); 438 desc->xid = 1; 439 } 440 441 int 442 efinet_get(struct iodesc *desc, void *pkt, size_t len, time_t tmo) 443 { 444 EFI_SIMPLE_NETWORK *net = NET; 445 EFI_STATUS status; 446 UINTN bufsz, pktsz; 447 time_t t; 448 char *buf, *ptr; 449 ssize_t ret = -1; 450 451 if (net == NULL) 452 return ret; 453 454 bufsz = net->Mode->MaxPacketSize + ETHER_HDR_LEN + ETHER_CRC_LEN; 455 buf = alloc(bufsz + ETHER_ALIGN); 456 if (buf == NULL) 457 return ret; 458 ptr = buf + ETHER_ALIGN; 459 460 t = getsecs(); 461 status = EFI_NOT_READY; 462 while ((getsecs() - t) < tmo) { 463 pktsz = bufsz; 464 status = EFI_CALL(net->Receive, net, NULL, &pktsz, ptr, 465 NULL, NULL, NULL); 466 if (status == EFI_SUCCESS) 467 break; 468 if (status != EFI_NOT_READY) 469 break; 470 } 471 472 if (status == EFI_SUCCESS) { 473 memcpy(pkt, ptr, min((ssize_t)pktsz, len)); 474 ret = min((ssize_t)pktsz, len); 475 } 476 477 free(buf, bufsz + ETHER_ALIGN); 478 return ret; 479 } 480 481 int 482 efinet_put(struct iodesc *desc, void *pkt, size_t len) 483 { 484 EFI_SIMPLE_NETWORK *net = NET; 485 EFI_STATUS status; 486 void *buf = NULL; 487 int ret = -1; 488 489 if (net == NULL) 490 goto out; 491 492 if (len > RECV_SIZE) 493 goto out; 494 495 memcpy((void *)txbuf, pkt, len); 496 status = EFI_CALL(net->Transmit, net, 0, len, (void *)txbuf, 497 NULL, NULL, NULL); 498 if (status != EFI_SUCCESS) 499 goto out; 500 501 buf = NULL; 502 while (status == EFI_SUCCESS) { 503 status = EFI_CALL(net->GetStatus, net, NULL, &buf); 504 if (buf) 505 break; 506 } 507 508 if (status == EFI_SUCCESS) 509 ret = len; 510 511 out: 512 return ret; 513 } 514 515 void 516 efinet_end(struct netif *nif) 517 { 518 EFI_SIMPLE_NETWORK *net = NET; 519 520 if (net == NULL) 521 return; 522 523 EFI_CALL(net->Shutdown, net); 524 } 525