diff options
Diffstat (limited to 'clients/net-snk/app/netapps/netboot.c')
-rw-r--r-- | clients/net-snk/app/netapps/netboot.c | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/clients/net-snk/app/netapps/netboot.c b/clients/net-snk/app/netapps/netboot.c new file mode 100644 index 0000000..07a1392 --- /dev/null +++ b/clients/net-snk/app/netapps/netboot.c @@ -0,0 +1,502 @@ +/****************************************************************************** + * Copyright (c) 2004, 2007 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <netlib/netlib.h> +#include <netlib/netbase.h> +#include <netlib/icmp.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <netapps/args.h> +#include <libbootmsg/libbootmsg.h> +#include <sys/socket.h> +#include <of.h> + +#define IP_INIT_DEFAULT 2 +#define IP_INIT_NONE 0 +#define IP_INIT_BOOTP 1 +#define IP_INIT_DHCP 2 + +#define DEFAULT_BOOT_RETRIES 600 +#define DEFAULT_TFTP_RETRIES 20 + +typedef struct { + int ip_init; + char siaddr[4]; + char filename[100]; + char ciaddr[4]; + char giaddr[4]; + int bootp_retries; + int tftp_retries; +} obp_tftp_args_t; + +/** + * Parses a argument string which is given by netload, extracts all + * parameters and fills a structure according to this + * + * Netload-Parameters: + * [bootp,]siaddr,filename,ciaddr,giaddr,bootp-retries,tftp-retries + * + * @param arg_str string with arguments, seperated with ',' + * @param obp_tftp_args structure which contains the result + * @return none + */ +static void parse_args(const char *arg_str, obp_tftp_args_t *obp_tftp_args) { + unsigned int argc; + char arg_buf[100]; + char *ptr; + + argc = get_args_count(arg_str); + + // find out if we should use BOOTP or DHCP + if(argc==0) + obp_tftp_args->ip_init = IP_INIT_DEFAULT; + else { + argncpy(arg_str, 0, arg_buf, 100); + if(strcasecmp(arg_buf, "bootp") == 0) { + obp_tftp_args->ip_init = IP_INIT_BOOTP; + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(strcasecmp(arg_buf, "dhcp") == 0) { + obp_tftp_args->ip_init = IP_INIT_DHCP; + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else + obp_tftp_args->ip_init = IP_INIT_DEFAULT; + } + + // find out siaddr + if(argc==0) + memset(obp_tftp_args->siaddr, 0, 4); + else { + argncpy(arg_str, 0, arg_buf, 100); + if(strtoip(arg_buf, obp_tftp_args->siaddr)) { + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(arg_buf[0] == 0) { + memset(obp_tftp_args->siaddr, 0, 4); + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else + memset(obp_tftp_args->siaddr, 0, 4); + } + + // find out filename + if(argc==0) + obp_tftp_args->filename[0] = 0; + else { + argncpy(arg_str, 0, obp_tftp_args->filename, 100); + for(ptr = obp_tftp_args->filename; *ptr != 0; ++ptr) + if(*ptr == '\\') + *ptr = '/'; + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + + // find out ciaddr + if(argc==0) + memset(obp_tftp_args->ciaddr, 0, 4); + else { + argncpy(arg_str, 0, arg_buf, 100); + if(strtoip(arg_buf, obp_tftp_args->ciaddr)) { + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(arg_buf[0] == 0) { + memset(obp_tftp_args->ciaddr, 0, 4); + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else + memset(obp_tftp_args->ciaddr, 0, 4); + } + + // find out giaddr + if(argc==0) + memset(obp_tftp_args->giaddr, 0, 4); + else { + argncpy(arg_str, 0, arg_buf, 100); + if(strtoip(arg_buf, obp_tftp_args->giaddr)) { + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(arg_buf[0] == 0) { + memset(obp_tftp_args->giaddr, 0, 4); + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else + memset(obp_tftp_args->giaddr, 0, 4); + } + + // find out bootp-retries + if(argc==0) + obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES; + else { + argncpy(arg_str, 0, arg_buf, 100); + if(arg_buf[0] == 0) + obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES; + else { + obp_tftp_args->bootp_retries = strtol(arg_buf, 0, 10); + if(obp_tftp_args->bootp_retries < 0) + obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES; + } + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + + // find out tftp-retries + if(argc==0) + obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES; + else { + argncpy(arg_str, 0, arg_buf, 100); + if(arg_buf[0] == 0) + obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES; + else { + obp_tftp_args->tftp_retries = strtol(arg_buf, 0, 10); + if(obp_tftp_args->tftp_retries < 0) + obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES; + } + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } +} + +int +netboot(int argc, char *argv[]) +{ + char buf[256]; + int rc; + int len = strtol(argv[2], 0, 16); + char *buffer = (char *) strtol(argv[1], 0, 16); + filename_ip_t fn_ip; + int fd_device; + tftp_err_t tftp_err; + obp_tftp_args_t obp_tftp_args; + char null_ip[4] = { 0x00, 0x00, 0x00, 0x00 }; + + printf("\n"); + printf(" Bootloader 1.5 \n"); + memset(&fn_ip, 0, sizeof(filename_ip_t)); + + /*********************************************************** + * + * Initialize network stuff and retrieve boot informations + * + ***********************************************************/ + + /* Wait for link up and get mac_addr from device */ + for(rc=0; rc<DEFAULT_BOOT_RETRIES; ++rc) { + if(rc > 0) { + set_timer(TICKS_SEC); + while (get_timer() > 0); + } + fd_device = socket(0, 0, 0, (char *) fn_ip.own_mac); + if(fd_device != -2) + break; + if(getchar() == 27) { + fd_device = -2; + break; + } + } + + if (fd_device == -1) { + strcpy(buf,"E3000: (net) Could not read MAC address"); + bootmsg_error(0x3000, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -100; + } + else if (fd_device == -2) { + strcpy(buf,"E3006: (net) Could not initialize network device"); + bootmsg_error(0x3006, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -101; + } + + printf(" Reading MAC address from device: " + "%02x:%02x:%02x:%02x:%02x:%02x\n", + fn_ip.own_mac[0], fn_ip.own_mac[1], fn_ip.own_mac[2], + fn_ip.own_mac[3], fn_ip.own_mac[4], fn_ip.own_mac[5]); + + if (argc >= 4) { + parse_args(argv[3], &obp_tftp_args); + if(obp_tftp_args.bootp_retries - rc < DEFAULT_BOOT_RETRIES) + obp_tftp_args.bootp_retries = DEFAULT_BOOT_RETRIES; + else + obp_tftp_args.bootp_retries -= rc; + } + else { + memset(&obp_tftp_args, 0, sizeof(obp_tftp_args_t)); + obp_tftp_args.ip_init = IP_INIT_DEFAULT; + obp_tftp_args.bootp_retries = DEFAULT_BOOT_RETRIES; + obp_tftp_args.tftp_retries = DEFAULT_TFTP_RETRIES; + } + memcpy(&fn_ip.own_ip, obp_tftp_args.ciaddr, 4); + + // init network stack + netbase_init(fd_device, fn_ip.own_mac, fn_ip.own_ip); + + // reset of error code + rc = 0; + + /* if we still have got all necessary parameters, then we don't + need to perform an BOOTP/DHCP-Request */ + if(memcmp(obp_tftp_args.ciaddr, null_ip, 4) != 0 + && memcmp(obp_tftp_args.siaddr, null_ip, 4) != 0 + && obp_tftp_args.filename[0] != 0) { + memcpy(&fn_ip.server_ip, obp_tftp_args.siaddr, 4); + + // try to get the MAC address of the TFTP server + if (net_iptomac(fn_ip.server_ip, fn_ip.server_mac)) { + // we got it + obp_tftp_args.ip_init = IP_INIT_NONE; + } + else { + // figure out if there is a change to get it somehow else + switch(obp_tftp_args.ip_init) { + case IP_INIT_NONE: + case IP_INIT_BOOTP: // BOOTP doesn't help + obp_tftp_args.ip_init = IP_INIT_NONE; + rc = -2; + break; + case IP_INIT_DHCP: // the DHCP server might tell us an + // appropriate router and netmask + default: + break; + } + } + } + + // construction of fn_ip from parameter + switch(obp_tftp_args.ip_init) { + case IP_INIT_BOOTP: + printf(" Requesting IP address via BOOTP: "); + // if giaddr in not specified, then we have to identify + // the BOOTP server via broadcasts + if(memcmp(obp_tftp_args.giaddr, null_ip, 4) == 0) { + // don't do this, when using DHCP !!! + fn_ip.server_ip = 0xFFFFFFFF; + memset(fn_ip.server_mac, 0xff, 6); + } + // if giaddr is specified, then we have to use this + // IP address as proxy to identify the BOOTP server + else { + memcpy(&fn_ip.server_ip, obp_tftp_args.giaddr, 4); + memset(fn_ip.server_mac, 0xff, 6); + } + rc = bootp(fd_device, &fn_ip, obp_tftp_args.bootp_retries); + break; + case IP_INIT_DHCP: + printf(" Requesting IP address via DHCP: "); + rc = dhcp(fd_device, &fn_ip, obp_tftp_args.bootp_retries); + break; + case IP_INIT_NONE: + default: + break; + } + + if(rc >= 0) { + if(memcmp(obp_tftp_args.ciaddr, null_ip, 4) != 0 + && memcmp(obp_tftp_args.ciaddr, &fn_ip.own_ip, 4) != 0) + memcpy(&fn_ip.own_ip, obp_tftp_args.ciaddr, 4); + + if(memcmp(obp_tftp_args.siaddr, null_ip, 4) != 0 + && memcmp(obp_tftp_args.siaddr, &fn_ip.server_ip, 4) != 0) + memcpy(&fn_ip.server_ip, obp_tftp_args.siaddr, 4); + + // reinit network stack + netbase_init(fd_device, fn_ip.own_mac, fn_ip.own_ip); + + if (!net_iptomac(fn_ip.server_ip, fn_ip.server_mac)) { + // printf("\nERROR:\t\t\tCan't obtain TFTP server MAC!\n"); + rc = -2; + } + } + + if (rc == -1) { + strcpy(buf,"E3001: (net) Could not get IP address"); + bootmsg_error(0x3001, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -101; + } + + printf("%d.%d.%d.%d\n", + ((fn_ip.own_ip >> 24) & 0xFF), ((fn_ip.own_ip >> 16) & 0xFF), + ((fn_ip.own_ip >> 8) & 0xFF), ( fn_ip.own_ip & 0xFF)); + + if (rc == -2) { + sprintf(buf, + "E3002: (net) ARP request to TFTP server " + "(%d.%d.%d.%d) failed", + ((fn_ip.server_ip >> 24) & 0xFF), + ((fn_ip.server_ip >> 16) & 0xFF), + ((fn_ip.server_ip >> 8) & 0xFF), + ( fn_ip.server_ip & 0xFF)); + bootmsg_error(0x3002, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -102; + } + if (rc == -4 || rc == -3) { + strcpy(buf,"E3008: (net) Can't obtain TFTP server IP address"); + bootmsg_error(0x3008, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -107; + } + + + /*********************************************************** + * + * Load file via TFTP into buffer provided by OpenFirmware + * + ***********************************************************/ + + if (obp_tftp_args.filename[0] != 0) { + strncpy((char *) fn_ip.filename, obp_tftp_args.filename, sizeof(fn_ip.filename)-1); + fn_ip.filename[sizeof(fn_ip.filename)-1] = 0; + } + + printf(" Requesting file \"%s\" via TFTP\n", fn_ip.filename); + + // accept at most 20 bad packets + // wait at most for 40 packets + rc = tftp(fd_device, &fn_ip, (unsigned char *) buffer, len, obp_tftp_args.tftp_retries, &tftp_err); + + if(obp_tftp_args.ip_init == IP_INIT_DHCP) + dhcp_send_release(); + + if (rc > 0) { + printf(" TFTP: Received %s (%d KBytes)\n", fn_ip.filename, + rc / 1024); + } else if (rc == -1) { + bootmsg_error(0x3003, "(net) unknown TFTP error"); + return -103; + } else if (rc == -2) { + sprintf(buf, + "E3004: (net) TFTP buffer of %d bytes " + "is too small for %s", + len, fn_ip.filename); + bootmsg_error(0x3004, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -104; + } else if (rc == -3) { + sprintf(buf,"E3009: (net) file not found: %s", + fn_ip.filename); + bootmsg_error(0x3009, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -108; + } else if (rc == -4) { + strcpy(buf,"E3010: (net) TFTP access violation"); + bootmsg_error(0x3010, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -109; + } else if (rc == -5) { + strcpy(buf,"E3011: (net) illegal TFTP operation"); + bootmsg_error(0x3011, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -110; + } else if (rc == -6) { + strcpy(buf, "E3012: (net) unknown TFTP transfer ID"); + bootmsg_error(0x3012, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -111; + } else if (rc == -7) { + strcpy(buf, "E3013: (net) no such TFTP user"); + bootmsg_error(0x3013, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -112; + } else if (rc == -8) { + strcpy(buf, "E3017: (net) TFTP blocksize negotiation failed"); + bootmsg_error(0x3017, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -116; + } else if (rc == -9) { + strcpy(buf,"E3018: (net) file exceeds maximum TFTP transfer size"); + bootmsg_error(0x3018, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -117; + } else if (rc <= -10 && rc >= -15) { + sprintf(buf,"E3005: (net) ICMP ERROR \""); + switch (rc) { + case -ICMP_NET_UNREACHABLE - 10: + sprintf(buf+strlen(buf),"net unreachable"); + break; + case -ICMP_HOST_UNREACHABLE - 10: + sprintf(buf+strlen(buf),"host unreachable"); + break; + case -ICMP_PROTOCOL_UNREACHABLE - 10: + sprintf(buf+strlen(buf),"protocol unreachable"); + break; + case -ICMP_PORT_UNREACHABLE - 10: + sprintf(buf+strlen(buf),"port unreachable"); + break; + case -ICMP_FRAGMENTATION_NEEDED - 10: + sprintf(buf+strlen(buf),"fragmentation needed and DF set"); + break; + case -ICMP_SOURCE_ROUTE_FAILED - 10: + sprintf(buf+strlen(buf),"source route failed"); + break; + default: + sprintf(buf+strlen(buf)," UNKNOWN"); + break; + } + sprintf(buf+strlen(buf),"\""); + bootmsg_error(0x3005, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -105; + } else if (rc == -40) { + sprintf(buf, + "E3014: (net) TFTP error occurred after " + "%d bad packets received", + tftp_err.bad_tftp_packets); + bootmsg_error(0x3014, &buf[7]); + write_mm_log(buf, strlen(buf), 0x91); + return -113; + } else if (rc == -41) { + sprintf(buf, + "E3015: (net) TFTP error occurred after " + "missing %d responses", + tftp_err.no_packets); + bootmsg_error(0x3015, &buf[7]); + write_mm_log(buf, strlen(buf), 0x91); + return -114; + } else if (rc == -42) { + sprintf(buf, + "E3016: (net) TFTP error missing block %d, " + "expected block was %d", + tftp_err.blocks_missed, + tftp_err.blocks_received); + bootmsg_error(0x3016, &buf[7]); + write_mm_log(buf, strlen(buf), 0x91); + return -115; + } + return rc; +} |