1 /* 2 * S390 virtio-ccw network boot loading program 3 * 4 * Copyright 2017 Thomas Huth, Red Hat Inc. 5 * 6 * Based on the S390 virtio-ccw loading program (main.c) 7 * Copyright (c) 2013 Alexander Graf <agraf@suse.de> 8 * 9 * And based on the network loading code from SLOF (netload.c) 10 * Copyright (c) 2004, 2008 IBM Corporation 11 * 12 * This code is free software; you can redistribute it and/or modify it 13 * under the terms of the GNU General Public License as published by the 14 * Free Software Foundation; either version 2 of the License, or (at your 15 * option) any later version. 16 */ 17 18 #include <stdint.h> 19 #include <stdbool.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <unistd.h> 24 25 #include <tftp.h> 26 #include <ethernet.h> 27 #include <dhcp.h> 28 #include <dhcpv6.h> 29 #include <ipv4.h> 30 #include <ipv6.h> 31 #include <dns.h> 32 #include <time.h> 33 34 #include "s390-ccw.h" 35 #include "virtio.h" 36 37 #define DEFAULT_BOOT_RETRIES 10 38 #define DEFAULT_TFTP_RETRIES 20 39 40 extern char _start[]; 41 42 char stack[PAGE_SIZE * 8] __attribute__((aligned(PAGE_SIZE))); 43 IplParameterBlock iplb __attribute__((aligned(PAGE_SIZE))); 44 45 static SubChannelId net_schid = { .one = 1 }; 46 static int ip_version = 4; 47 static uint64_t dest_timer; 48 49 static uint64_t get_timer_ms(void) 50 { 51 uint64_t clk; 52 53 asm volatile(" stck %0 " : : "Q"(clk) : "memory"); 54 55 /* Bit 51 is incremented each microsecond */ 56 return (clk >> (63 - 51)) / 1000; 57 } 58 59 void set_timer(int val) 60 { 61 dest_timer = get_timer_ms() + val; 62 } 63 64 int get_timer(void) 65 { 66 return dest_timer - get_timer_ms(); 67 } 68 69 int get_sec_ticks(void) 70 { 71 return 1000; /* number of ticks in 1 second */ 72 } 73 74 /** 75 * Obtain IP and configuration info from DHCP server (either IPv4 or IPv6). 76 * @param fn_ip contains the following configuration information: 77 * client MAC, client IP, TFTP-server MAC, TFTP-server IP, 78 * boot file name 79 * @param retries Number of DHCP attempts 80 * @return 0 : IP and configuration info obtained; 81 * non-0 : error condition occurred. 82 */ 83 static int dhcp(struct filename_ip *fn_ip, int retries) 84 { 85 int i = retries + 1; 86 int rc = -1; 87 88 printf(" Requesting information via DHCP: "); 89 90 dhcpv4_generate_transaction_id(); 91 dhcpv6_generate_transaction_id(); 92 93 do { 94 printf("\b\b\b%03d", i - 1); 95 if (!--i) { 96 printf("\nGiving up after %d DHCP requests\n", retries); 97 return -1; 98 } 99 ip_version = 4; 100 rc = dhcpv4(NULL, fn_ip); 101 if (rc == -1) { 102 ip_version = 6; 103 set_ipv6_address(fn_ip->fd, 0); 104 rc = dhcpv6(NULL, fn_ip); 105 if (rc == 0) { 106 memcpy(&fn_ip->own_ip6, get_ipv6_address(), 16); 107 break; 108 } 109 } 110 if (rc != -1) { /* either success or non-dhcp failure */ 111 break; 112 } 113 } while (1); 114 printf("\b\b\b\bdone\n"); 115 116 return rc; 117 } 118 119 /** 120 * Seed the random number generator with our mac and current timestamp 121 */ 122 static void seed_rng(uint8_t mac[]) 123 { 124 uint64_t seed; 125 126 asm volatile(" stck %0 " : : "Q"(seed) : "memory"); 127 seed ^= (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5]; 128 srand(seed); 129 } 130 131 static int tftp_load(filename_ip_t *fnip, void *buffer, int len, 132 unsigned int retries, int ip_vers) 133 { 134 tftp_err_t tftp_err; 135 int rc; 136 137 rc = tftp(fnip, buffer, len, retries, &tftp_err, 1, 1428, ip_vers); 138 139 if (rc > 0) { 140 printf(" TFTP: Received %s (%d KBytes)\n", fnip->filename, 141 rc / 1024); 142 } else if (rc == -1) { 143 puts("unknown TFTP error"); 144 } else if (rc == -2) { 145 printf("TFTP buffer of %d bytes is too small for %s\n", 146 len, fnip->filename); 147 } else if (rc == -3) { 148 printf("file not found: %s\n", fnip->filename); 149 } else if (rc == -4) { 150 puts("TFTP access violation"); 151 } else if (rc == -5) { 152 puts("illegal TFTP operation"); 153 } else if (rc == -6) { 154 puts("unknown TFTP transfer ID"); 155 } else if (rc == -7) { 156 puts("no such TFTP user"); 157 } else if (rc == -8) { 158 puts("TFTP blocksize negotiation failed"); 159 } else if (rc == -9) { 160 puts("file exceeds maximum TFTP transfer size"); 161 } else if (rc <= -10 && rc >= -15) { 162 const char *icmp_err_str; 163 switch (rc) { 164 case -ICMP_NET_UNREACHABLE - 10: 165 icmp_err_str = "net unreachable"; 166 break; 167 case -ICMP_HOST_UNREACHABLE - 10: 168 icmp_err_str = "host unreachable"; 169 break; 170 case -ICMP_PROTOCOL_UNREACHABLE - 10: 171 icmp_err_str = "protocol unreachable"; 172 break; 173 case -ICMP_PORT_UNREACHABLE - 10: 174 icmp_err_str = "port unreachable"; 175 break; 176 case -ICMP_FRAGMENTATION_NEEDED - 10: 177 icmp_err_str = "fragmentation needed and DF set"; 178 break; 179 case -ICMP_SOURCE_ROUTE_FAILED - 10: 180 icmp_err_str = "source route failed"; 181 break; 182 default: 183 icmp_err_str = " UNKNOWN"; 184 break; 185 } 186 printf("ICMP ERROR \"%s\"\n", icmp_err_str); 187 } else if (rc == -40) { 188 printf("TFTP error occurred after %d bad packets received", 189 tftp_err.bad_tftp_packets); 190 } else if (rc == -41) { 191 printf("TFTP error occurred after missing %d responses", 192 tftp_err.no_packets); 193 } else if (rc == -42) { 194 printf("TFTP error missing block %d, expected block was %d", 195 tftp_err.blocks_missed, 196 tftp_err.blocks_received); 197 } 198 199 return rc; 200 } 201 202 static int net_load(char *buffer, int len) 203 { 204 filename_ip_t fn_ip; 205 uint8_t mac[6]; 206 int rc; 207 208 memset(&fn_ip, 0, sizeof(filename_ip_t)); 209 210 rc = virtio_net_init(mac); 211 if (rc < 0) { 212 puts("Could not initialize network device"); 213 return -101; 214 } 215 fn_ip.fd = rc; 216 217 printf(" Using MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", 218 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 219 220 set_mac_address(mac); /* init ethernet layer */ 221 seed_rng(mac); 222 223 rc = dhcp(&fn_ip, DEFAULT_BOOT_RETRIES); 224 if (rc >= 0) { 225 if (ip_version == 4) { 226 set_ipv4_address(fn_ip.own_ip); 227 } 228 } else { 229 puts("Could not get IP address"); 230 return -101; 231 } 232 233 if (ip_version == 4) { 234 printf(" Using IPv4 address: %d.%d.%d.%d\n", 235 (fn_ip.own_ip >> 24) & 0xFF, (fn_ip.own_ip >> 16) & 0xFF, 236 (fn_ip.own_ip >> 8) & 0xFF, fn_ip.own_ip & 0xFF); 237 } else if (ip_version == 6) { 238 char ip6_str[40]; 239 ipv6_to_str(fn_ip.own_ip6.addr, ip6_str); 240 printf(" Using IPv6 address: %s\n", ip6_str); 241 } 242 243 if (rc == -2) { 244 printf("ARP request to TFTP server (%d.%d.%d.%d) failed\n", 245 (fn_ip.server_ip >> 24) & 0xFF, (fn_ip.server_ip >> 16) & 0xFF, 246 (fn_ip.server_ip >> 8) & 0xFF, fn_ip.server_ip & 0xFF); 247 return -102; 248 } 249 if (rc == -4 || rc == -3) { 250 puts("Can't obtain TFTP server IP address"); 251 return -107; 252 } 253 254 if (ip_version == 4) { 255 printf(" Requesting file \"%s\" via TFTP from %d.%d.%d.%d\n", 256 fn_ip.filename, 257 (fn_ip.server_ip >> 24) & 0xFF, (fn_ip.server_ip >> 16) & 0xFF, 258 (fn_ip.server_ip >> 8) & 0xFF, fn_ip.server_ip & 0xFF); 259 } else if (ip_version == 6) { 260 char ip6_str[40]; 261 printf(" Requesting file \"%s\" via TFTP from ", fn_ip.filename); 262 ipv6_to_str(fn_ip.server_ip6.addr, ip6_str); 263 printf("%s\n", ip6_str); 264 } 265 266 /* Do the TFTP load and print error message if necessary */ 267 rc = tftp_load(&fn_ip, buffer, len, DEFAULT_TFTP_RETRIES, ip_version); 268 269 if (ip_version == 4) { 270 dhcp_send_release(fn_ip.fd); 271 } 272 273 return rc; 274 } 275 276 void panic(const char *string) 277 { 278 sclp_print(string); 279 for (;;) { 280 disabled_wait(); 281 } 282 } 283 284 static bool find_net_dev(Schib *schib, int dev_no) 285 { 286 int i, r; 287 288 for (i = 0; i < 0x10000; i++) { 289 net_schid.sch_no = i; 290 r = stsch_err(net_schid, schib); 291 if (r == 3 || r == -EIO) { 292 break; 293 } 294 if (!schib->pmcw.dnv) { 295 continue; 296 } 297 if (!virtio_is_supported(net_schid)) { 298 continue; 299 } 300 if (virtio_get_device_type() != VIRTIO_ID_NET) { 301 continue; 302 } 303 if (dev_no < 0 || schib->pmcw.dev == dev_no) { 304 return true; 305 } 306 } 307 308 return false; 309 } 310 311 static void virtio_setup(void) 312 { 313 Schib schib; 314 int ssid; 315 bool found = false; 316 uint16_t dev_no; 317 318 /* 319 * We unconditionally enable mss support. In every sane configuration, 320 * this will succeed; and even if it doesn't, stsch_err() can deal 321 * with the consequences. 322 */ 323 enable_mss_facility(); 324 325 if (store_iplb(&iplb)) { 326 IPL_assert(iplb.pbt == S390_IPL_TYPE_CCW, "IPL_TYPE_CCW expected"); 327 dev_no = iplb.ccw.devno; 328 debug_print_int("device no. ", dev_no); 329 net_schid.ssid = iplb.ccw.ssid & 0x3; 330 debug_print_int("ssid ", net_schid.ssid); 331 found = find_net_dev(&schib, dev_no); 332 } else { 333 for (ssid = 0; ssid < 0x3; ssid++) { 334 net_schid.ssid = ssid; 335 found = find_net_dev(&schib, -1); 336 if (found) { 337 break; 338 } 339 } 340 } 341 342 IPL_assert(found, "No virtio net device found"); 343 } 344 345 void main(void) 346 { 347 int rc; 348 349 sclp_setup(); 350 sclp_print("Network boot starting...\n"); 351 352 virtio_setup(); 353 354 rc = net_load(NULL, (long)_start); 355 if (rc > 0) { 356 sclp_print("Network loading done, starting kernel...\n"); 357 asm volatile (" lpsw 0(%0) " : : "r"(0) : "memory"); 358 } 359 360 panic("Failed to load OS from network\n"); 361 } 362