| /* |
| * Copyright 2013 Google Inc. |
| * |
| * See file CREDITS for list of people who contributed to this |
| * project. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of |
| * the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but without any warranty; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
| * MA 02111-1307 USA |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "base/algorithm.h" |
| #include "boot/boot.h" |
| #include "drivers/net/net.h" |
| #include "net/ethernet.h" |
| #include "net/netboot/dhcp.h" |
| #include "net/netboot/netboot.h" |
| #include "net/netboot/params.h" |
| #include "net/netboot/tftp.h" |
| #include "net/ipv4/ipv4.h" |
| #include "net/ipv4/uip/arp.h" |
| #include "net/ipv4/uip/uip.h" |
| |
| static void print_ip_addr(const uip_ipaddr_t *ip) |
| { |
| printf("%d.%d.%d.%d", uip_ipaddr1(ip), uip_ipaddr2(ip), |
| uip_ipaddr3(ip), uip_ipaddr4(ip)); |
| } |
| |
| static void print_mac_addr(const MacAddress *mac) |
| { |
| for (int i = 0; i < ARRAY_SIZE(mac->octet); i++) |
| printf("%s%02x", i ? ":" : "", mac->octet[i]); |
| } |
| |
| static void * const payload = (void *)(uintptr_t)CONFIG_KERNEL_START; |
| static const uint32_t MaxPayloadSize = CONFIG_KERNEL_SIZE; |
| |
| char cmd_line[4096]; |
| |
| static void netboot_attempt(NetDevice *dev, uip_ipaddr_t *tftp_ip, |
| char *bootfile, char *argsfile, char *args) |
| { |
| // Plug in the MAC address. |
| const MacAddress *mac_addr = dev->get_mac(dev); |
| if (!mac_addr) |
| return; |
| printf("MAC: "); |
| print_mac_addr(mac_addr); |
| printf("\n"); |
| uip_setethaddr(*mac_addr); |
| |
| // Find out who we are. |
| uip_ipaddr_t my_ip, next_ip, server_ip; |
| const char *dhcp_bootfile; |
| if (dhcp_request(dev, &next_ip, &server_ip, &dhcp_bootfile)) |
| return; |
| |
| printf("My ip is "); |
| uip_gethostaddr(&my_ip); |
| print_ip_addr(&my_ip); |
| printf("\nThe DHCP server ip is "); |
| print_ip_addr(&server_ip); |
| printf("\n"); |
| |
| if (!tftp_ip) { |
| tftp_ip = &next_ip; |
| printf("TFTP server IP supplied by DHCP server: "); |
| } else { |
| printf("TFTP server IP predefined by user: "); |
| } |
| print_ip_addr(tftp_ip); |
| printf("\n"); |
| |
| // Download the bootfile. |
| if (!bootfile) { |
| bootfile = (char *)dhcp_bootfile; |
| printf("Bootfile supplied by DHCP server: %s\n", bootfile); |
| } else { |
| printf("Bootfile predefined by user: %s\n", bootfile); |
| } |
| |
| Ipv4UdpCon *tftp = new_ipv4_udp_con(dev, *tftp_ip, TftpPort); |
| |
| uint32_t size; |
| if (tftp_read(&tftp->ops, payload, bootfile, &size, MaxPayloadSize)) { |
| printf("Tftp failed.\n"); |
| netcon_close(&tftp->ops); |
| free(tftp); |
| if (dhcp_release(dev, server_ip)) |
| printf("Dhcp release failed.\n"); |
| return; |
| } |
| printf("The bootfile was %d bytes long.\n", size); |
| |
| // Try to download command line file via TFTP if argsfile is specified |
| if (argsfile && !(tftp_read(&tftp->ops, cmd_line, argsfile, &size, |
| sizeof(cmd_line) - 1))) { |
| while (cmd_line[size - 1] <= ' ') // strip trailing whitespace |
| if (!--size) break; // and control chars (\n, \r) |
| cmd_line[size] = '\0'; |
| while (size--) // replace inline control |
| if (cmd_line[size] < ' ') // chars with spaces |
| cmd_line[size] = ' '; |
| printf("Command line loaded dynamically from TFTP file: %s\n", |
| argsfile); |
| // If that fails or file wasn't specified fall back to args parameter |
| } else if (args) { |
| strncpy(cmd_line, args, sizeof(cmd_line) - 1); |
| printf("Command line predefined by user.\n"); |
| } |
| |
| netcon_close(&tftp->ops); |
| free(tftp); |
| |
| // We're done on the network, so release our IP. |
| if (dhcp_release(dev, server_ip)) { |
| printf("Dhcp release failed.\n"); |
| return; |
| } |
| |
| // Add tftp server IP into command line. |
| static const char def_tftp_cmdline[] = " tftpserverip=xxx.xxx.xxx.xxx"; |
| int cmd_line_size = strlen(cmd_line); |
| if (cmd_line_size + sizeof(def_tftp_cmdline) - 1 >= sizeof(cmd_line)) { |
| printf("Out of space adding TFTP server IP " \ |
| "to the command line.\n"); |
| return; |
| } |
| sprintf(&cmd_line[cmd_line_size], " tftpserverip=%d.%d.%d.%d", |
| uip_ipaddr1(tftp_ip), uip_ipaddr2(tftp_ip), |
| uip_ipaddr3(tftp_ip), uip_ipaddr4(tftp_ip)); |
| printf("The command line is: %s\n", cmd_line); |
| |
| // Boot. |
| boot(payload, cmd_line, NULL, NULL); |
| } |
| |
| void netboot(char *args, size_t args_size) |
| { |
| // Start up the network stack. |
| uip_init(); |
| |
| uip_ipaddr_t *tftp_ip; |
| char *bootfile; |
| char *argsfile; |
| if (netboot_params_read(&tftp_ip, args, args_size, |
| &bootfile, &argsfile)) { |
| printf("Failed to read netboot parameters from flash.\n"); |
| } |
| |
| NetDevice *dev = NULL; |
| while (1) { |
| dev = net_scan_for_link(dev); |
| if (dev) { |
| netboot_attempt(dev, tftp_ip, bootfile, |
| argsfile, args); |
| } else { |
| net_find_devices(); |
| } |
| } |
| } |