1/* 2 * Copyright (c) 2001-2013 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29/* 30 * History: 31 * 14 December, 2001 Dieter Siegmund (dieter@apple.com) 32 * - created 33 */ 34#include <sys/param.h> 35#include <sys/systm.h> 36#include <sys/kernel.h> 37#include <sys/conf.h> 38#include <sys/ioctl.h> 39#include <sys/proc_internal.h> 40#include <sys/mount_internal.h> 41#include <sys/mbuf.h> 42#include <sys/filedesc.h> 43#include <sys/vnode_internal.h> 44#include <sys/malloc.h> 45#include <sys/socket.h> 46#include <sys/socketvar.h> 47#include <sys/reboot.h> 48#include <sys/kauth.h> 49#include <net/if.h> 50#include <net/if_dl.h> 51#include <net/if_types.h> 52#include <net/route.h> 53#include <netinet/in.h> 54#include <netinet/if_ether.h> 55#include <netinet/dhcp_options.h> 56#include <netinet/in_dhcp.h> 57 58#include <kern/kern_types.h> 59#include <kern/kalloc.h> 60#include <sys/netboot.h> 61#include <sys/imageboot.h> 62#include <pexpert/pexpert.h> 63 64//#include <libkern/libkern.h> 65extern struct filedesc filedesc0; 66 67extern int nfs_mountroot(void); /* nfs_vfsops.c */ 68extern int (*mountroot)(void); 69 70extern unsigned char rootdevice[]; 71 72static int S_netboot = 0; 73static struct netboot_info * S_netboot_info_p; 74 75void * 76IOBSDRegistryEntryForDeviceTree(const char * path); 77 78void 79IOBSDRegistryEntryRelease(void * entry); 80 81const void * 82IOBSDRegistryEntryGetData(void * entry, const char * property_name, 83 int * packet_length); 84 85#define BOOTP_RESPONSE "bootp-response" 86#define BSDP_RESPONSE "bsdp-response" 87#define DHCP_RESPONSE "dhcp-response" 88 89#define IP_FORMAT "%d.%d.%d.%d" 90#define IP_CH(ip) ((u_char *)ip) 91#define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3] 92 93#define kNetBootRootPathPrefixNFS "nfs:" 94#define kNetBootRootPathPrefixHTTP "http:" 95 96typedef enum { 97 kNetBootImageTypeUnknown = 0, 98 kNetBootImageTypeNFS = 1, 99 kNetBootImageTypeHTTP = 2, 100} NetBootImageType; 101 102struct netboot_info { 103 struct in_addr client_ip; 104 struct in_addr server_ip; 105 char * server_name; 106 int server_name_length; 107 char * mount_point; 108 int mount_point_length; 109 char * image_path; 110 int image_path_length; 111 NetBootImageType image_type; 112 char * second_image_path; 113 int second_image_path_length; 114}; 115 116/* 117 * Function: parse_booter_path 118 * Purpose: 119 * Parse a string of the form: 120 * "<IP>:<host>:<mount>[:<image_path>]" 121 * into the given ip address, host, mount point, and optionally, image_path. 122 * 123 * Note: 124 * The passed in string is modified i.e. ':' is replaced by '\0'. 125 * Example: 126 * "17.202.16.17:seaport:/release/.images/Image9/CurrentHera" 127 */ 128static __inline__ boolean_t 129parse_booter_path(char * path, struct in_addr * iaddr_p, char const * * host, 130 char * * mount_dir, char * * image_path) 131{ 132 char * start; 133 char * colon; 134 135 /* IP address */ 136 start = path; 137 colon = strchr(start, ':'); 138 if (colon == NULL) { 139 return (FALSE); 140 } 141 *colon = '\0'; 142 if (inet_aton(start, iaddr_p) != 1) { 143 return (FALSE); 144 } 145 146 /* host */ 147 start = colon + 1; 148 colon = strchr(start, ':'); 149 if (colon == NULL) { 150 return (FALSE); 151 } 152 *colon = '\0'; 153 *host = start; 154 155 /* mount */ 156 start = colon + 1; 157 colon = strchr(start, ':'); 158 *mount_dir = start; 159 if (colon == NULL) { 160 *image_path = NULL; 161 } 162 else { 163 /* image path */ 164 *colon = '\0'; 165 start = colon + 1; 166 *image_path = start; 167 } 168 return (TRUE); 169} 170 171/* 172 * Function: find_colon 173 * Purpose: 174 * Find the next unescaped instance of the colon character. 175 * If a colon is escaped (preceded by a backslash '\' character), 176 * shift the string over by one character to overwrite the backslash. 177 */ 178static __inline__ char * 179find_colon(char * str) 180{ 181 char * start = str; 182 char * colon; 183 184 while ((colon = strchr(start, ':')) != NULL) { 185 char * dst; 186 char * src; 187 188 if (colon == start) { 189 break; 190 } 191 if (colon[-1] != '\\') 192 break; 193 for (dst = colon - 1, src = colon; *dst != '\0'; dst++, src++) { 194 *dst = *src; 195 } 196 start = colon; 197 } 198 return (colon); 199} 200 201/* 202 * Function: parse_netboot_path 203 * Purpose: 204 * Parse a string of the form: 205 * "nfs:<IP>:<mount>[:<image_path>]" 206 * into the given ip address, host, mount point, and optionally, image_path. 207 * Notes: 208 * - the passed in string is modified i.e. ':' is replaced by '\0' 209 * - literal colons must be escaped with a backslash 210 * 211 * Examples: 212 * nfs:17.202.42.112:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg 213 * nfs:17.202.42.112:/Volumes/Foo\:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg 214 */ 215static __inline__ boolean_t 216parse_netboot_path(char * path, struct in_addr * iaddr_p, char const * * host, 217 char * * mount_dir, char * * image_path) 218{ 219 static char tmp[MAX_IPv4_STR_LEN]; /* Danger - not thread safe */ 220 char * start; 221 char * colon; 222 223 if (strncmp(path, kNetBootRootPathPrefixNFS, 224 strlen(kNetBootRootPathPrefixNFS)) != 0) { 225 return (FALSE); 226 } 227 228 /* IP address */ 229 start = path + strlen(kNetBootRootPathPrefixNFS); 230 colon = strchr(start, ':'); 231 if (colon == NULL) { 232 return (FALSE); 233 } 234 *colon = '\0'; 235 if (inet_aton(start, iaddr_p) != 1) { 236 return (FALSE); 237 } 238 239 /* mount point */ 240 start = colon + 1; 241 colon = find_colon(start); 242 *mount_dir = start; 243 if (colon == NULL) { 244 *image_path = NULL; 245 } 246 else { 247 /* image path */ 248 *colon = '\0'; 249 start = colon + 1; 250 (void)find_colon(start); 251 *image_path = start; 252 } 253 *host = inet_ntop(AF_INET, iaddr_p, tmp, sizeof(tmp)); 254 return (TRUE); 255} 256 257static boolean_t 258parse_image_path(char * path, struct in_addr * iaddr_p, char const * * host, 259 char * * mount_dir, char * * image_path) 260{ 261 if (path[0] >= '0' && path[0] <= '9') { 262 return (parse_booter_path(path, iaddr_p, host, mount_dir, 263 image_path)); 264 } 265 return (parse_netboot_path(path, iaddr_p, host, mount_dir, 266 image_path)); 267} 268 269static boolean_t 270get_root_path(char * root_path) 271{ 272 void * entry; 273 boolean_t found = FALSE; 274 const void * pkt; 275 int pkt_len; 276 277 entry = IOBSDRegistryEntryForDeviceTree("/chosen"); 278 if (entry == NULL) { 279 return (FALSE); 280 } 281 pkt = IOBSDRegistryEntryGetData(entry, BSDP_RESPONSE, &pkt_len); 282 if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) { 283 printf("netboot: retrieving root path from BSDP response\n"); 284 } 285 else { 286 pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE, 287 &pkt_len); 288 if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) { 289 printf("netboot: retrieving root path from BOOTP response\n"); 290 } 291 } 292 if (pkt != NULL) { 293 int len; 294 dhcpol_t options; 295 const char * path; 296 const struct dhcp * reply; 297 298 reply = (const struct dhcp *)pkt; 299 (void)dhcpol_parse_packet(&options, reply, pkt_len); 300 301 path = (const char *)dhcpol_find(&options, 302 dhcptag_root_path_e, &len, NULL); 303 if (path) { 304 memcpy(root_path, path, len); 305 root_path[len] = '\0'; 306 found = TRUE; 307 } 308 } 309 IOBSDRegistryEntryRelease(entry); 310 return (found); 311 312} 313 314static void 315save_path(char * * str_p, int * length_p, char * path) 316{ 317 *length_p = strlen(path) + 1; 318 *str_p = (char *)kalloc(*length_p); 319 strlcpy(*str_p, path, *length_p); 320 return; 321} 322 323static struct netboot_info * 324netboot_info_init(struct in_addr iaddr) 325{ 326 boolean_t have_root_path = FALSE; 327 struct netboot_info * info = NULL; 328 char * root_path = NULL; 329 330 info = (struct netboot_info *)kalloc(sizeof(*info)); 331 bzero(info, sizeof(*info)); 332 info->client_ip = iaddr; 333 info->image_type = kNetBootImageTypeUnknown; 334 335 /* check for a booter-specified path then a NetBoot path */ 336 MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); 337 if (root_path == NULL) 338 panic("netboot_info_init: M_NAMEI zone exhausted"); 339 if (PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) == TRUE 340 || PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == TRUE 341 || PE_parse_boot_argn("rootpath", root_path, MAXPATHLEN) == TRUE) { 342 if (imageboot_format_is_valid(root_path)) { 343 printf("netboot_info_init: rp0='%s' isn't a network path," 344 " ignoring\n", root_path); 345 } 346 else { 347 have_root_path = TRUE; 348 } 349 } 350 if (have_root_path == FALSE) { 351 have_root_path = get_root_path(root_path); 352 } 353 if (have_root_path) { 354 const char * server_name = NULL; 355 char * mount_point = NULL; 356 char * image_path = NULL; 357 struct in_addr server_ip; 358 359 if (parse_image_path(root_path, &server_ip, &server_name, 360 &mount_point, &image_path)) { 361 info->image_type = kNetBootImageTypeNFS; 362 info->server_ip = server_ip; 363 info->server_name_length = strlen(server_name) + 1; 364 info->server_name = (char *)kalloc(info->server_name_length); 365 info->mount_point_length = strlen(mount_point) + 1; 366 info->mount_point = (char *)kalloc(info->mount_point_length); 367 strlcpy(info->server_name, server_name, info->server_name_length); 368 strlcpy(info->mount_point, mount_point, info->mount_point_length); 369 370 printf("netboot: NFS Server %s Mount %s", 371 server_name, info->mount_point); 372 if (image_path != NULL) { 373 boolean_t needs_slash = FALSE; 374 375 info->image_path_length = strlen(image_path) + 1; 376 if (image_path[0] != '/') { 377 needs_slash = TRUE; 378 info->image_path_length++; 379 } 380 info->image_path = (char *)kalloc(info->image_path_length); 381 if (needs_slash) { 382 info->image_path[0] = '/'; 383 strlcpy(info->image_path + 1, image_path, 384 info->image_path_length - 1); 385 } else { 386 strlcpy(info->image_path, image_path, 387 info->image_path_length); 388 } 389 printf(" Image %s", info->image_path); 390 } 391 printf("\n"); 392 } 393 else if (strncmp(root_path, kNetBootRootPathPrefixHTTP, 394 strlen(kNetBootRootPathPrefixHTTP)) == 0) { 395 info->image_type = kNetBootImageTypeHTTP; 396 save_path(&info->image_path, &info->image_path_length, 397 root_path); 398 printf("netboot: HTTP URL %s\n", info->image_path); 399 } 400 else { 401 printf("netboot: root path uses unrecognized format\n"); 402 } 403 404 /* check for image-within-image */ 405 if (info->image_path != NULL) { 406 if (PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) 407 || PE_parse_boot_argn("rp1", root_path, MAXPATHLEN)) { 408 /* rp1/root-dmg is the second-level image */ 409 save_path(&info->second_image_path, &info->second_image_path_length, 410 root_path); 411 } 412 } 413 if (info->second_image_path != NULL) { 414 printf("netboot: nested image %s\n", info->second_image_path); 415 } 416 } 417 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI); 418 return (info); 419} 420 421static void 422netboot_info_free(struct netboot_info * * info_p) 423{ 424 struct netboot_info * info = *info_p; 425 426 if (info) { 427 if (info->mount_point) { 428 kfree(info->mount_point, info->mount_point_length); 429 } 430 if (info->server_name) { 431 kfree(info->server_name, info->server_name_length); 432 } 433 if (info->image_path) { 434 kfree(info->image_path, info->image_path_length); 435 } 436 if (info->second_image_path) { 437 kfree(info->second_image_path, info->second_image_path_length); 438 } 439 kfree(info, sizeof(*info)); 440 } 441 *info_p = NULL; 442 return; 443} 444 445boolean_t 446netboot_iaddr(struct in_addr * iaddr_p) 447{ 448 if (S_netboot_info_p == NULL) 449 return (FALSE); 450 451 *iaddr_p = S_netboot_info_p->client_ip; 452 return (TRUE); 453} 454 455boolean_t 456netboot_rootpath(struct in_addr * server_ip, 457 char * name, int name_len, 458 char * path, int path_len) 459{ 460 if (S_netboot_info_p == NULL) 461 return (FALSE); 462 463 name[0] = '\0'; 464 path[0] = '\0'; 465 466 if (S_netboot_info_p->mount_point_length == 0) { 467 return (FALSE); 468 } 469 if (path_len < S_netboot_info_p->mount_point_length) { 470 printf("netboot: path too small %d < %d\n", 471 path_len, S_netboot_info_p->mount_point_length); 472 return (FALSE); 473 } 474 strlcpy(path, S_netboot_info_p->mount_point, path_len); 475 strlcpy(name, S_netboot_info_p->server_name, name_len); 476 *server_ip = S_netboot_info_p->server_ip; 477 return (TRUE); 478} 479 480 481static boolean_t 482get_ip_parameters(struct in_addr * iaddr_p, struct in_addr * netmask_p, 483 struct in_addr * router_p) 484{ 485 void * entry; 486 const void * pkt; 487 int pkt_len; 488 489 490 entry = IOBSDRegistryEntryForDeviceTree("/chosen"); 491 if (entry == NULL) { 492 return (FALSE); 493 } 494 pkt = IOBSDRegistryEntryGetData(entry, DHCP_RESPONSE, &pkt_len); 495 if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) { 496 printf("netboot: retrieving IP information from DHCP response\n"); 497 } 498 else { 499 pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE, &pkt_len); 500 if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) { 501 printf("netboot: retrieving IP information from BOOTP response\n"); 502 } 503 } 504 if (pkt != NULL) { 505 const struct in_addr * ip; 506 int len; 507 dhcpol_t options; 508 const struct dhcp * reply; 509 510 reply = (const struct dhcp *)pkt; 511 (void)dhcpol_parse_packet(&options, reply, pkt_len); 512 *iaddr_p = reply->dp_yiaddr; 513 ip = (const struct in_addr *) 514 dhcpol_find(&options, 515 dhcptag_subnet_mask_e, &len, NULL); 516 if (ip) { 517 *netmask_p = *ip; 518 } 519 ip = (const struct in_addr *) 520 dhcpol_find(&options, dhcptag_router_e, &len, NULL); 521 if (ip) { 522 *router_p = *ip; 523 } 524 } 525 IOBSDRegistryEntryRelease(entry); 526 return (pkt != NULL); 527} 528 529static int 530route_cmd(int cmd, struct in_addr d, struct in_addr g, 531 struct in_addr m, uint32_t more_flags, unsigned int ifscope) 532{ 533 struct sockaddr_in dst; 534 int error; 535 uint32_t flags = RTF_UP | RTF_STATIC; 536 struct sockaddr_in gw; 537 struct sockaddr_in mask; 538 539 flags |= more_flags; 540 541 /* destination */ 542 bzero((caddr_t)&dst, sizeof(dst)); 543 dst.sin_len = sizeof(dst); 544 dst.sin_family = AF_INET; 545 dst.sin_addr = d; 546 547 /* gateway */ 548 bzero((caddr_t)&gw, sizeof(gw)); 549 gw.sin_len = sizeof(gw); 550 gw.sin_family = AF_INET; 551 gw.sin_addr = g; 552 553 /* mask */ 554 bzero(&mask, sizeof(mask)); 555 mask.sin_len = sizeof(mask); 556 mask.sin_family = AF_INET; 557 mask.sin_addr = m; 558 559 error = rtrequest_scoped(cmd, (struct sockaddr *)&dst, 560 (struct sockaddr *)&gw, (struct sockaddr *)&mask, flags, NULL, ifscope); 561 562 return (error); 563 564} 565 566static int 567default_route_add(struct in_addr router, boolean_t proxy_arp) 568{ 569 uint32_t flags = 0; 570 struct in_addr zeroes = { 0 }; 571 572 if (proxy_arp == FALSE) { 573 flags |= RTF_GATEWAY; 574 } 575 return (route_cmd(RTM_ADD, zeroes, router, zeroes, flags, IFSCOPE_NONE)); 576} 577 578static int 579host_route_delete(struct in_addr host, unsigned int ifscope) 580{ 581 struct in_addr zeroes = { 0 }; 582 583 return (route_cmd(RTM_DELETE, host, zeroes, zeroes, RTF_HOST, ifscope)); 584} 585 586static struct ifnet * 587find_interface(void) 588{ 589 struct ifnet * ifp = NULL; 590 591 dlil_if_lock(); 592 if (rootdevice[0]) { 593 ifp = ifunit((char *)rootdevice); 594 } 595 if (ifp == NULL) { 596 ifnet_head_lock_shared(); 597 TAILQ_FOREACH(ifp, &ifnet_head, if_link) 598 if ((ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0) 599 break; 600 ifnet_head_done(); 601 } 602 dlil_if_unlock(); 603 return (ifp); 604} 605 606int 607netboot_mountroot(void) 608{ 609 int error = 0; 610 struct in_addr iaddr = { 0 }; 611 struct ifreq ifr; 612 struct ifnet * ifp; 613 struct in_addr netmask = { 0 }; 614 proc_t procp = current_proc(); 615 struct in_addr router = { 0 }; 616 struct socket * so = NULL; 617 unsigned int try; 618 619 bzero(&ifr, sizeof(ifr)); 620 621 /* find the interface */ 622 ifp = find_interface(); 623 if (ifp == NULL) { 624 printf("netboot: no suitable interface\n"); 625 error = ENXIO; 626 goto failed; 627 } 628 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", if_name(ifp)); 629 printf("netboot: using network interface '%s'\n", ifr.ifr_name); 630 631 /* bring it up */ 632 if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0) { 633 printf("netboot: socreate, error=%d\n", error); 634 goto failed; 635 } 636 ifr.ifr_flags = ifp->if_flags | IFF_UP; 637 error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ifr, procp); 638 if (error) { 639 printf("netboot: SIFFLAGS, error=%d\n", error); 640 goto failed; 641 } 642 643 /* grab information from the registry */ 644 if (get_ip_parameters(&iaddr, &netmask, &router) == FALSE) { 645 /* use DHCP to retrieve IP address, netmask and router */ 646 error = dhcp(ifp, &iaddr, 64, &netmask, &router, procp); 647 if (error) { 648 printf("netboot: DHCP failed %d\n", error); 649 goto failed; 650 } 651 } 652 printf("netboot: IP address " IP_FORMAT, IP_LIST(&iaddr)); 653 if (netmask.s_addr) { 654 printf(" netmask " IP_FORMAT, IP_LIST(&netmask)); 655 } 656 if (router.s_addr) { 657 printf(" router " IP_FORMAT, IP_LIST(&router)); 658 } 659 printf("\n"); 660 error = inet_aifaddr(so, ifr.ifr_name, &iaddr, &netmask, NULL); 661 if (error) { 662 printf("netboot: inet_aifaddr failed, %d\n", error); 663 goto failed; 664 } 665 if (router.s_addr == 0) { 666 /* enable proxy arp if we don't have a router */ 667 router.s_addr = iaddr.s_addr; 668 } 669 printf("netboot: adding default route " IP_FORMAT "\n", 670 IP_LIST(&router)); 671 error = default_route_add(router, router.s_addr == iaddr.s_addr); 672 if (error) { 673 printf("netboot: default_route_add failed %d\n", error); 674 } 675 676 soclose(so); 677 678 S_netboot_info_p = netboot_info_init(iaddr); 679 switch (S_netboot_info_p->image_type) { 680 default: 681 case kNetBootImageTypeNFS: 682 for (try = 1; TRUE; try++) { 683 error = nfs_mountroot(); 684 if (error == 0) { 685 break; 686 } 687 printf("netboot: nfs_mountroot() attempt %u failed; " 688 "clearing ARP entry and trying again\n", try); 689 /* 690 * error is either EHOSTDOWN or EHOSTUNREACH, which likely means 691 * that the port we're plugged into has spanning tree enabled, 692 * and either the router or the server can't answer our ARP 693 * requests. Clear the incomplete ARP entry by removing the 694 * appropriate route, depending on the error code: 695 * EHOSTDOWN NFS server's route 696 * EHOSTUNREACH router's route 697 */ 698 switch (error) { 699 default: 700 /* NOT REACHED */ 701 case EHOSTDOWN: 702 /* remove the server's arp entry */ 703 error = host_route_delete(S_netboot_info_p->server_ip, 704 ifp->if_index); 705 if (error) { 706 printf("netboot: host_route_delete(" IP_FORMAT 707 ") failed %d\n", 708 IP_LIST(&S_netboot_info_p->server_ip), error); 709 } 710 break; 711 case EHOSTUNREACH: 712 error = host_route_delete(router, ifp->if_index); 713 if (error) { 714 printf("netboot: host_route_delete(" IP_FORMAT 715 ") failed %d\n", IP_LIST(&router), error); 716 } 717 break; 718 } 719 } 720 break; 721 case kNetBootImageTypeHTTP: 722 error = netboot_setup(); 723 break; 724 } 725 if (error == 0) { 726 S_netboot = 1; 727 } 728 else { 729 S_netboot = 0; 730 } 731 return (error); 732failed: 733 if (so != NULL) { 734 soclose(so); 735 } 736 return (error); 737} 738 739int 740netboot_setup() 741{ 742 int error = 0; 743 744 if (S_netboot_info_p == NULL 745 || S_netboot_info_p->image_path == NULL) { 746 goto done; 747 } 748 printf("netboot_setup: calling imageboot_mount_image\n"); 749 error = imageboot_mount_image(S_netboot_info_p->image_path, -1); 750 if (error != 0) { 751 printf("netboot: failed to mount root image, %d\n", error); 752 } 753 else if (S_netboot_info_p->second_image_path != NULL) { 754 error = imageboot_mount_image(S_netboot_info_p->second_image_path, 0); 755 if (error != 0) { 756 printf("netboot: failed to mount second root image, %d\n", error); 757 } 758 } 759 760 done: 761 netboot_info_free(&S_netboot_info_p); 762 return (error); 763} 764 765int 766netboot_root(void) 767{ 768 return (S_netboot); 769} 770