1 | #include <dprintf.h> |
---|
2 | #include <stdio.h> |
---|
3 | #include <string.h> |
---|
4 | #include <core.h> |
---|
5 | #include <fs.h> |
---|
6 | #include <fcntl.h> |
---|
7 | #include <sys/cpu.h> |
---|
8 | #include "pxe.h" |
---|
9 | #include "thread.h" |
---|
10 | #include "url.h" |
---|
11 | #include "tftp.h" |
---|
12 | #include <net.h> |
---|
13 | |
---|
14 | __lowmem t_PXENV_UNDI_GET_INFORMATION pxe_undi_info; |
---|
15 | __lowmem t_PXENV_UNDI_GET_IFACE_INFO pxe_undi_iface; |
---|
16 | |
---|
17 | uint8_t MAC[MAC_MAX]; /* Actual MAC address */ |
---|
18 | uint8_t MAC_len; /* MAC address len */ |
---|
19 | uint8_t MAC_type; /* MAC address type */ |
---|
20 | |
---|
21 | char boot_file[256]; /* From DHCP */ |
---|
22 | char path_prefix[256]; /* From DHCP */ |
---|
23 | |
---|
24 | bool have_uuid = false; |
---|
25 | |
---|
26 | /* |
---|
27 | * Allocate a local UDP port structure and assign it a local port number. |
---|
28 | * Return the inode pointer if success, or null if failure |
---|
29 | */ |
---|
30 | static struct inode *allocate_socket(struct fs_info *fs) |
---|
31 | { |
---|
32 | struct inode *inode = alloc_inode(fs, 0, sizeof(struct pxe_pvt_inode)); |
---|
33 | |
---|
34 | if (!inode) { |
---|
35 | malloc_error("socket structure"); |
---|
36 | } else { |
---|
37 | inode->mode = DT_REG; /* No other types relevant for PXE */ |
---|
38 | } |
---|
39 | |
---|
40 | return inode; |
---|
41 | } |
---|
42 | |
---|
43 | void free_socket(struct inode *inode) |
---|
44 | { |
---|
45 | struct pxe_pvt_inode *socket = PVT(inode); |
---|
46 | |
---|
47 | free(socket->tftp_pktbuf); /* If we allocated a buffer, free it now */ |
---|
48 | free_inode(inode); |
---|
49 | } |
---|
50 | |
---|
51 | static void pxe_close_file(struct file *file) |
---|
52 | { |
---|
53 | struct inode *inode = file->inode; |
---|
54 | struct pxe_pvt_inode *socket = PVT(inode); |
---|
55 | |
---|
56 | if (!inode) |
---|
57 | return; |
---|
58 | |
---|
59 | if (!socket->tftp_goteof) { |
---|
60 | socket->ops->close(inode); |
---|
61 | } |
---|
62 | |
---|
63 | free_socket(inode); |
---|
64 | } |
---|
65 | |
---|
66 | /* |
---|
67 | * Tests an IP address in _ip_ for validity; return with 0 for bad, 1 for good. |
---|
68 | * We used to refuse class E, but class E addresses are likely to become |
---|
69 | * assignable unicast addresses in the near future. |
---|
70 | * |
---|
71 | */ |
---|
72 | bool ip_ok(uint32_t ip) |
---|
73 | { |
---|
74 | uint8_t ip_hi = (uint8_t)ip; /* First octet of the ip address */ |
---|
75 | |
---|
76 | if (ip == 0xffffffff || /* Refuse the all-ones address */ |
---|
77 | ip_hi == 0 || /* Refuse network zero */ |
---|
78 | ip_hi == 127 || /* Refuse the loopback network */ |
---|
79 | (ip_hi & 240) == 224) /* Refuse class D */ |
---|
80 | return false; |
---|
81 | |
---|
82 | return true; |
---|
83 | } |
---|
84 | |
---|
85 | |
---|
86 | /* |
---|
87 | * Take an IP address (in network byte order) in _ip_ and |
---|
88 | * output a dotted quad string to _dst_, returns the length |
---|
89 | * of the dotted quad ip string. |
---|
90 | * |
---|
91 | */ |
---|
92 | static int gendotquad(char *dst, uint32_t ip) |
---|
93 | { |
---|
94 | return sprintf(dst, "%u.%u.%u.%u", |
---|
95 | ((const uint8_t *)&ip)[0], |
---|
96 | ((const uint8_t *)&ip)[1], |
---|
97 | ((const uint8_t *)&ip)[2], |
---|
98 | ((const uint8_t *)&ip)[3]); |
---|
99 | } |
---|
100 | |
---|
101 | /* |
---|
102 | * the ASM pxenv function wrapper, return 1 if error, or 0 |
---|
103 | * |
---|
104 | */ |
---|
105 | __export int pxe_call(int opcode, void *data) |
---|
106 | { |
---|
107 | static DECLARE_INIT_SEMAPHORE(pxe_sem, 1); |
---|
108 | extern void pxenv(void); |
---|
109 | com32sys_t regs; |
---|
110 | |
---|
111 | sem_down(&pxe_sem, 0); |
---|
112 | |
---|
113 | #if 0 |
---|
114 | dprintf("pxe_call op %04x data %p\n", opcode, data); |
---|
115 | #endif |
---|
116 | |
---|
117 | memset(®s, 0, sizeof regs); |
---|
118 | regs.ebx.w[0] = opcode; |
---|
119 | regs.es = SEG(data); |
---|
120 | regs.edi.w[0] = OFFS(data); |
---|
121 | call16(pxenv, ®s, ®s); |
---|
122 | |
---|
123 | sem_up(&pxe_sem); |
---|
124 | |
---|
125 | return regs.eflags.l & EFLAGS_CF; /* CF SET if fail */ |
---|
126 | } |
---|
127 | |
---|
128 | /* |
---|
129 | * mangle a filename pointed to by _src_ into a buffer pointed |
---|
130 | * to by _dst_; ends on encountering any whitespace. |
---|
131 | * |
---|
132 | * This deliberately does not attempt to do any conversion of |
---|
133 | * pathname separators. |
---|
134 | * |
---|
135 | */ |
---|
136 | static void pxe_mangle_name(char *dst, const char *src) |
---|
137 | { |
---|
138 | size_t len = FILENAME_MAX-1; |
---|
139 | |
---|
140 | while (len-- && not_whitespace(*src)) |
---|
141 | *dst++ = *src++; |
---|
142 | |
---|
143 | *dst = '\0'; |
---|
144 | } |
---|
145 | |
---|
146 | /* |
---|
147 | * Read a single character from the specified pxe inode. |
---|
148 | * Very useful for stepping through http streams and |
---|
149 | * parsing their headers. |
---|
150 | */ |
---|
151 | int pxe_getc(struct inode *inode) |
---|
152 | { |
---|
153 | struct pxe_pvt_inode *socket = PVT(inode); |
---|
154 | unsigned char byte; |
---|
155 | |
---|
156 | while (!socket->tftp_bytesleft) { |
---|
157 | if (socket->tftp_goteof) |
---|
158 | return -1; |
---|
159 | |
---|
160 | socket->ops->fill_buffer(inode); |
---|
161 | } |
---|
162 | |
---|
163 | byte = *socket->tftp_dataptr; |
---|
164 | socket->tftp_bytesleft -= 1; |
---|
165 | socket->tftp_dataptr += 1; |
---|
166 | |
---|
167 | return byte; |
---|
168 | } |
---|
169 | |
---|
170 | /* |
---|
171 | * Get a fresh packet if the buffer is drained, and we haven't hit |
---|
172 | * EOF yet. The buffer should be filled immediately after draining! |
---|
173 | */ |
---|
174 | static void fill_buffer(struct inode *inode) |
---|
175 | { |
---|
176 | struct pxe_pvt_inode *socket = PVT(inode); |
---|
177 | if (socket->tftp_bytesleft || socket->tftp_goteof) |
---|
178 | return; |
---|
179 | |
---|
180 | return socket->ops->fill_buffer(inode); |
---|
181 | } |
---|
182 | |
---|
183 | |
---|
184 | /** |
---|
185 | * getfssec: Get multiple clusters from a file, given the starting cluster. |
---|
186 | * In this case, get multiple blocks from a specific TCP connection. |
---|
187 | * |
---|
188 | * @param: fs, the fs_info structure address, in pxe, we don't use this. |
---|
189 | * @param: buf, buffer to store the read data |
---|
190 | * @param: openfile, TFTP socket pointer |
---|
191 | * @param: blocks, 512-byte block count; 0FFFFh = until end of file |
---|
192 | * |
---|
193 | * @return: the bytes read |
---|
194 | * |
---|
195 | */ |
---|
196 | static uint32_t pxe_getfssec(struct file *file, char *buf, |
---|
197 | int blocks, bool *have_more) |
---|
198 | { |
---|
199 | struct inode *inode = file->inode; |
---|
200 | struct pxe_pvt_inode *socket = PVT(inode); |
---|
201 | int count = blocks; |
---|
202 | int chunk; |
---|
203 | int bytes_read = 0; |
---|
204 | |
---|
205 | count <<= TFTP_BLOCKSIZE_LG2; |
---|
206 | while (count) { |
---|
207 | fill_buffer(inode); /* If we have no 'fresh' buffer, get it */ |
---|
208 | if (!socket->tftp_bytesleft) |
---|
209 | break; |
---|
210 | |
---|
211 | chunk = count; |
---|
212 | if (chunk > socket->tftp_bytesleft) |
---|
213 | chunk = socket->tftp_bytesleft; |
---|
214 | socket->tftp_bytesleft -= chunk; |
---|
215 | memcpy(buf, socket->tftp_dataptr, chunk); |
---|
216 | socket->tftp_dataptr += chunk; |
---|
217 | buf += chunk; |
---|
218 | bytes_read += chunk; |
---|
219 | count -= chunk; |
---|
220 | } |
---|
221 | |
---|
222 | |
---|
223 | if (socket->tftp_bytesleft || (socket->tftp_filepos < inode->size)) { |
---|
224 | fill_buffer(inode); |
---|
225 | *have_more = 1; |
---|
226 | } else if (socket->tftp_goteof) { |
---|
227 | /* |
---|
228 | * The socket is closed and the buffer drained; the caller will |
---|
229 | * call close_file and therefore free the socket. |
---|
230 | */ |
---|
231 | *have_more = 0; |
---|
232 | } |
---|
233 | |
---|
234 | return bytes_read; |
---|
235 | } |
---|
236 | |
---|
237 | /* |
---|
238 | * Assign an IP address to a URL |
---|
239 | */ |
---|
240 | static void url_set_ip(struct url_info *url) |
---|
241 | { |
---|
242 | url->ip = 0; |
---|
243 | if (url->host) |
---|
244 | url->ip = dns_resolv(url->host); |
---|
245 | if (!url->ip) |
---|
246 | url->ip = IPInfo.serverip; |
---|
247 | } |
---|
248 | |
---|
249 | /** |
---|
250 | * Open the specified connection |
---|
251 | * |
---|
252 | * @param:filename, the file we wanna open |
---|
253 | * |
---|
254 | * @out: open_file_t structure, stores in file->open_file |
---|
255 | * @out: the lenght of this file, stores in file->file_len |
---|
256 | * |
---|
257 | */ |
---|
258 | static void __pxe_searchdir(const char *filename, int flags, struct file *file); |
---|
259 | extern uint16_t PXERetry; |
---|
260 | |
---|
261 | static void pxe_searchdir(const char *filename, int flags, struct file *file) |
---|
262 | { |
---|
263 | int i = PXERetry; |
---|
264 | |
---|
265 | do { |
---|
266 | dprintf("PXE: file = %p, retries left = %d: ", file, i); |
---|
267 | __pxe_searchdir(filename, flags, file); |
---|
268 | dprintf("%s\n", file->inode ? "ok" : "failed"); |
---|
269 | } while (!file->inode && i--); |
---|
270 | } |
---|
271 | static void __pxe_searchdir(const char *filename, int flags, struct file *file) |
---|
272 | { |
---|
273 | struct fs_info *fs = file->fs; |
---|
274 | struct inode *inode; |
---|
275 | char fullpath[2*FILENAME_MAX]; |
---|
276 | #if GPXE |
---|
277 | char urlsave[2*FILENAME_MAX]; |
---|
278 | #endif |
---|
279 | struct url_info url; |
---|
280 | const struct url_scheme *us = NULL; |
---|
281 | int redirect_count = 0; |
---|
282 | bool found_scheme = false; |
---|
283 | |
---|
284 | inode = file->inode = NULL; |
---|
285 | |
---|
286 | while (filename) { |
---|
287 | if (redirect_count++ > 5) |
---|
288 | break; |
---|
289 | |
---|
290 | strlcpy(fullpath, filename, sizeof fullpath); |
---|
291 | #if GPXE |
---|
292 | strcpy(urlsave, fullpath); |
---|
293 | #endif |
---|
294 | parse_url(&url, fullpath); |
---|
295 | if (url.type == URL_SUFFIX) { |
---|
296 | snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename); |
---|
297 | #if GPXE |
---|
298 | strcpy(urlsave, fullpath); |
---|
299 | #endif |
---|
300 | parse_url(&url, fullpath); |
---|
301 | } |
---|
302 | |
---|
303 | inode = allocate_socket(fs); |
---|
304 | if (!inode) |
---|
305 | return; /* Allocation failure */ |
---|
306 | |
---|
307 | url_set_ip(&url); |
---|
308 | |
---|
309 | filename = NULL; |
---|
310 | found_scheme = false; |
---|
311 | for (us = url_schemes; us->name; us++) { |
---|
312 | if (!strcmp(us->name, url.scheme)) { |
---|
313 | if ((flags & ~us->ok_flags & OK_FLAGS_MASK) == 0) |
---|
314 | us->open(&url, flags, inode, &filename); |
---|
315 | found_scheme = true; |
---|
316 | break; |
---|
317 | } |
---|
318 | } |
---|
319 | |
---|
320 | /* filename here is set on a redirect */ |
---|
321 | } |
---|
322 | |
---|
323 | if (!found_scheme) { |
---|
324 | #if GPXE |
---|
325 | /* No URL scheme found, hand it to GPXE */ |
---|
326 | gpxe_open(inode, urlsave); |
---|
327 | #endif |
---|
328 | } |
---|
329 | |
---|
330 | if (inode->size) { |
---|
331 | file->inode = inode; |
---|
332 | file->inode->mode = (flags & O_DIRECTORY) ? DT_DIR : DT_REG; |
---|
333 | } else { |
---|
334 | free_socket(inode); |
---|
335 | } |
---|
336 | |
---|
337 | return; |
---|
338 | } |
---|
339 | |
---|
340 | |
---|
341 | /* |
---|
342 | * Store standard filename prefix |
---|
343 | */ |
---|
344 | static void get_prefix(void) |
---|
345 | { |
---|
346 | int len; |
---|
347 | char *p; |
---|
348 | char c; |
---|
349 | |
---|
350 | if (!(DHCPMagic & 0x04)) { |
---|
351 | /* No path prefix option, derive from boot file */ |
---|
352 | |
---|
353 | strlcpy(path_prefix, boot_file, sizeof path_prefix); |
---|
354 | len = strlen(path_prefix); |
---|
355 | p = &path_prefix[len - 1]; |
---|
356 | |
---|
357 | while (len--) { |
---|
358 | c = *p--; |
---|
359 | c |= 0x20; |
---|
360 | |
---|
361 | c = (c >= '0' && c <= '9') || |
---|
362 | (c >= 'a' && c <= 'z') || |
---|
363 | (c == '.' || c == '-'); |
---|
364 | if (!c) |
---|
365 | break; |
---|
366 | }; |
---|
367 | |
---|
368 | if (len < 0) |
---|
369 | p --; |
---|
370 | |
---|
371 | *(p + 2) = 0; /* Zero-terminate after delimiter */ |
---|
372 | } |
---|
373 | |
---|
374 | ddprintf("TFTP prefix: %s\n", path_prefix); |
---|
375 | |
---|
376 | if (url_type(path_prefix) == URL_SUFFIX) { |
---|
377 | /* |
---|
378 | * Construct a ::-style TFTP path. |
---|
379 | * |
---|
380 | * We may have moved out of the root directory at the time |
---|
381 | * this function is invoked, but to maintain compatibility |
---|
382 | * with versions of Syslinux < 5.00, path_prefix must be |
---|
383 | * relative to "::". |
---|
384 | */ |
---|
385 | p = strdup(path_prefix); |
---|
386 | if (!p) |
---|
387 | return; |
---|
388 | |
---|
389 | snprintf(path_prefix, sizeof path_prefix, "::%s", p); |
---|
390 | free(p); |
---|
391 | } |
---|
392 | |
---|
393 | chdir(path_prefix); |
---|
394 | } |
---|
395 | |
---|
396 | /* |
---|
397 | * realpath for PXE |
---|
398 | */ |
---|
399 | static size_t pxe_realpath(struct fs_info *fs, char *dst, const char *src, |
---|
400 | size_t bufsize) |
---|
401 | { |
---|
402 | return snprintf(dst, bufsize, "%s%s", |
---|
403 | url_type(src) == URL_SUFFIX ? fs->cwd_name : "", src); |
---|
404 | } |
---|
405 | |
---|
406 | /* |
---|
407 | * chdir for PXE |
---|
408 | */ |
---|
409 | static int pxe_chdir(struct fs_info *fs, const char *src) |
---|
410 | { |
---|
411 | /* The cwd for PXE is just a text prefix */ |
---|
412 | enum url_type path_type = url_type(src); |
---|
413 | |
---|
414 | if (path_type == URL_SUFFIX) |
---|
415 | strlcat(fs->cwd_name, src, sizeof fs->cwd_name); |
---|
416 | else |
---|
417 | strlcpy(fs->cwd_name, src, sizeof fs->cwd_name); |
---|
418 | return 0; |
---|
419 | |
---|
420 | dprintf("cwd = \"%s\"\n", fs->cwd_name); |
---|
421 | return 0; |
---|
422 | } |
---|
423 | |
---|
424 | static int pxe_chdir_start(void) |
---|
425 | { |
---|
426 | get_prefix(); |
---|
427 | return 0; |
---|
428 | } |
---|
429 | |
---|
430 | /* Load the config file, return -1 if failed, or 0 */ |
---|
431 | static int pxe_open_config(struct com32_filedata *filedata) |
---|
432 | { |
---|
433 | const char *cfgprefix = "pxelinux.cfg/"; |
---|
434 | const char *default_str = "default"; |
---|
435 | char *config_file; |
---|
436 | char *last; |
---|
437 | int tries = 8; |
---|
438 | |
---|
439 | chdir(path_prefix); |
---|
440 | if (DHCPMagic & 0x02) { |
---|
441 | /* We got a DHCP option, try it first */ |
---|
442 | if (open_file(ConfigName, O_RDONLY, filedata) >= 0) |
---|
443 | return 0; |
---|
444 | } |
---|
445 | |
---|
446 | /* |
---|
447 | * Have to guess config file name ... |
---|
448 | */ |
---|
449 | config_file = stpcpy(ConfigName, cfgprefix); |
---|
450 | |
---|
451 | /* Try loading by UUID */ |
---|
452 | if (sysappend_strings[SYSAPPEND_SYSUUID]) { |
---|
453 | strcpy(config_file, sysappend_strings[SYSAPPEND_SYSUUID]+8); |
---|
454 | if (open_file(ConfigName, O_RDONLY, filedata) >= 0) |
---|
455 | return 0; |
---|
456 | } |
---|
457 | |
---|
458 | /* Try loading by MAC address */ |
---|
459 | strcpy(config_file, sysappend_strings[SYSAPPEND_BOOTIF]+7); |
---|
460 | if (open_file(ConfigName, O_RDONLY, filedata) >= 0) |
---|
461 | return 0; |
---|
462 | |
---|
463 | /* Nope, try hexadecimal IP prefixes... */ |
---|
464 | sprintf(config_file, "%08X", ntohl(IPInfo.myip)); |
---|
465 | last = &config_file[8]; |
---|
466 | while (tries) { |
---|
467 | *last = '\0'; /* Zero-terminate string */ |
---|
468 | if (open_file(ConfigName, O_RDONLY, filedata) >= 0) |
---|
469 | return 0; |
---|
470 | last--; /* Drop one character */ |
---|
471 | tries--; |
---|
472 | }; |
---|
473 | |
---|
474 | /* Final attempt: "default" string */ |
---|
475 | strcpy(config_file, default_str); |
---|
476 | if (open_file(ConfigName, O_RDONLY, filedata) >= 0) |
---|
477 | return 0; |
---|
478 | |
---|
479 | ddprintf("%-68s\n", "Unable to locate configuration file"); |
---|
480 | kaboom(); |
---|
481 | } |
---|
482 | |
---|
483 | /* |
---|
484 | * Generate the bootif string. |
---|
485 | */ |
---|
486 | static void make_bootif_string(void) |
---|
487 | { |
---|
488 | static char bootif_str[7+3*(MAC_MAX+1)]; |
---|
489 | const uint8_t *src; |
---|
490 | char *dst = bootif_str; |
---|
491 | int i; |
---|
492 | |
---|
493 | dst += sprintf(dst, "BOOTIF=%02x", MAC_type); |
---|
494 | src = MAC; |
---|
495 | for (i = MAC_len; i; i--) |
---|
496 | dst += sprintf(dst, "-%02x", *src++); |
---|
497 | |
---|
498 | sysappend_strings[SYSAPPEND_BOOTIF] = bootif_str; |
---|
499 | } |
---|
500 | |
---|
501 | /* |
---|
502 | * Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask> |
---|
503 | * option into IPOption based on DHCP information in IPInfo. |
---|
504 | * |
---|
505 | */ |
---|
506 | static void genipopt(void) |
---|
507 | { |
---|
508 | static char ip_option[3+4*16]; |
---|
509 | const uint32_t *v = &IPInfo.myip; |
---|
510 | char *p; |
---|
511 | int i; |
---|
512 | |
---|
513 | p = stpcpy(ip_option, "ip="); |
---|
514 | |
---|
515 | for (i = 0; i < 4; i++) { |
---|
516 | p += gendotquad(p, *v++); |
---|
517 | *p++ = ':'; |
---|
518 | } |
---|
519 | *--p = '\0'; |
---|
520 | |
---|
521 | sysappend_strings[SYSAPPEND_IP] = ip_option; |
---|
522 | } |
---|
523 | |
---|
524 | |
---|
525 | /* Generate ip= option and print the ip adress */ |
---|
526 | static void ip_init(void) |
---|
527 | { |
---|
528 | uint32_t ip = IPInfo.myip; |
---|
529 | char dot_quad_buf[16]; |
---|
530 | |
---|
531 | genipopt(); |
---|
532 | gendotquad(dot_quad_buf, ip); |
---|
533 | |
---|
534 | ip = ntohl(ip); |
---|
535 | ddprintf("My IP address seems to be %08X %s\n", ip, dot_quad_buf); |
---|
536 | } |
---|
537 | |
---|
538 | /* |
---|
539 | * Network-specific initialization |
---|
540 | */ |
---|
541 | static void network_init(void) |
---|
542 | { |
---|
543 | net_parse_dhcp(); |
---|
544 | |
---|
545 | make_bootif_string(); |
---|
546 | /* If DMI and DHCP disagree, which one should we set? */ |
---|
547 | if (have_uuid) |
---|
548 | sysappend_set_uuid(uuid); |
---|
549 | ip_init(); |
---|
550 | |
---|
551 | /* print_sysappend(); */ |
---|
552 | /* |
---|
553 | * Check to see if we got any PXELINUX-specific DHCP options; in particular, |
---|
554 | * if we didn't get the magic enable, do not recognize any other options. |
---|
555 | */ |
---|
556 | if ((DHCPMagic & 1) == 0) |
---|
557 | DHCPMagic = 0; |
---|
558 | |
---|
559 | net_core_init(); |
---|
560 | } |
---|
561 | |
---|
562 | /* |
---|
563 | * Initialize pxe fs |
---|
564 | * |
---|
565 | */ |
---|
566 | static int pxe_fs_init(struct fs_info *fs) |
---|
567 | { |
---|
568 | (void)fs; /* drop the compile warning message */ |
---|
569 | |
---|
570 | /* Prepare for handling pxe interrupts */ |
---|
571 | pxe_init_isr(); |
---|
572 | |
---|
573 | /* This block size is actually arbitrary... */ |
---|
574 | fs->sector_shift = fs->block_shift = TFTP_BLOCKSIZE_LG2; |
---|
575 | fs->sector_size = fs->block_size = 1 << TFTP_BLOCKSIZE_LG2; |
---|
576 | |
---|
577 | /* Find the PXE stack */ |
---|
578 | if (pxe_init(false)) |
---|
579 | kaboom(); |
---|
580 | |
---|
581 | /* See if we also have a gPXE stack */ |
---|
582 | gpxe_init(); |
---|
583 | |
---|
584 | /* Network-specific initialization */ |
---|
585 | network_init(); |
---|
586 | |
---|
587 | /* Initialize network-card-specific idle handling */ |
---|
588 | pxe_idle_init(); |
---|
589 | |
---|
590 | /* Our name for the root */ |
---|
591 | strcpy(fs->cwd_name, "::"); |
---|
592 | |
---|
593 | return 0; |
---|
594 | } |
---|
595 | |
---|
596 | /* |
---|
597 | * Look to see if we are on an EFI CSM system. Some EFI |
---|
598 | * CSM systems put the BEV stack in low memory, which means |
---|
599 | * a return to the PXE stack will crash the system. However, |
---|
600 | * INT 18h works reliably, so in that case hack the stack and |
---|
601 | * point the "return address" to an INT 18h instruction. |
---|
602 | * |
---|
603 | * Hack the stack instead of the much simpler "just invoke INT 18h |
---|
604 | * if we want to reset", so that chainloading other NBPs will work. |
---|
605 | * |
---|
606 | * This manipulates the real-mode InitStack directly. It relies on this |
---|
607 | * *not* being a currently active stack, i.e. the former |
---|
608 | * USE_PXE_PROVIDED_STACK no longer works. |
---|
609 | * |
---|
610 | * XXX: Disable this until we can find a better way to discriminate |
---|
611 | * between BIOSes that are broken on BEV return and BIOSes which are |
---|
612 | * broken on INT 18h. Keying on the EFI CSM turns out to cause more |
---|
613 | * problems than it solves. |
---|
614 | */ |
---|
615 | extern far_ptr_t InitStack; |
---|
616 | |
---|
617 | struct efi_struct { |
---|
618 | uint32_t magic; |
---|
619 | uint8_t csum; |
---|
620 | uint8_t len; |
---|
621 | } __attribute__((packed)); |
---|
622 | #define EFI_MAGIC (('$' << 24)+('E' << 16)+('F' << 8)+'I') |
---|
623 | |
---|
624 | static inline bool is_efi(const struct efi_struct *efi) |
---|
625 | { |
---|
626 | /* |
---|
627 | * We don't verify the checksum, because it seems some CSMs leave |
---|
628 | * it at zero, sigh... |
---|
629 | */ |
---|
630 | return (efi->magic == EFI_MAGIC) && (efi->len >= 83); |
---|
631 | } |
---|
632 | |
---|
633 | #if 0 |
---|
634 | static void install_int18_hack(void) |
---|
635 | { |
---|
636 | static const uint8_t int18_hack[] = |
---|
637 | { |
---|
638 | 0xcd, 0x18, /* int $0x18 */ |
---|
639 | 0xea, 0xf0, 0xff, 0x00, 0xf0, /* ljmpw $0xf000,$0xfff0 */ |
---|
640 | 0xf4 /* hlt */ |
---|
641 | }; |
---|
642 | uint16_t *retcode; |
---|
643 | |
---|
644 | retcode = GET_PTR(*(far_ptr_t *)((char *)GET_PTR(InitStack) + 44)); |
---|
645 | |
---|
646 | /* Don't do this if the return already points to int $0x18 */ |
---|
647 | if (*retcode != 0x18cd) { |
---|
648 | uint32_t efi_ptr; |
---|
649 | bool efi = false; |
---|
650 | |
---|
651 | for (efi_ptr = 0xe0000 ; efi_ptr < 0x100000 ; efi_ptr += 16) { |
---|
652 | if (is_efi((const struct efi_struct *)efi_ptr)) { |
---|
653 | efi = true; |
---|
654 | break; |
---|
655 | } |
---|
656 | } |
---|
657 | |
---|
658 | if (efi) { |
---|
659 | uint8_t *src = GET_PTR(InitStack); |
---|
660 | uint8_t *dst = src - sizeof int18_hack; |
---|
661 | |
---|
662 | memmove(dst, src, 52); |
---|
663 | memcpy(dst+52, int18_hack, sizeof int18_hack); |
---|
664 | InitStack.offs -= sizeof int18_hack; |
---|
665 | |
---|
666 | /* Clobber the return address */ |
---|
667 | *(uint16_t *)(dst+44) = OFFS_WRT(dst+52, InitStack.seg); |
---|
668 | *(uint16_t *)(dst+46) = InitStack.seg; |
---|
669 | } |
---|
670 | } |
---|
671 | } |
---|
672 | #endif |
---|
673 | |
---|
674 | static int pxe_readdir(struct file *file, struct dirent *dirent) |
---|
675 | { |
---|
676 | struct inode *inode = file->inode; |
---|
677 | struct pxe_pvt_inode *socket = PVT(inode); |
---|
678 | |
---|
679 | if (socket->ops->readdir) |
---|
680 | return socket->ops->readdir(inode, dirent); |
---|
681 | else |
---|
682 | return -1; /* No such operation */ |
---|
683 | } |
---|
684 | |
---|
685 | const struct fs_ops pxe_fs_ops = { |
---|
686 | .fs_name = "pxe", |
---|
687 | .fs_flags = FS_NODEV, |
---|
688 | .fs_init = pxe_fs_init, |
---|
689 | .searchdir = pxe_searchdir, |
---|
690 | .chdir = pxe_chdir, |
---|
691 | .realpath = pxe_realpath, |
---|
692 | .getfssec = pxe_getfssec, |
---|
693 | .close_file = pxe_close_file, |
---|
694 | .mangle_name = pxe_mangle_name, |
---|
695 | .chdir_start = pxe_chdir_start, |
---|
696 | .open_config = pxe_open_config, |
---|
697 | .readdir = pxe_readdir, |
---|
698 | .fs_uuid = NULL, |
---|
699 | }; |
---|