From 6708faafdea4e3b27879e089e772babb78bfe7f2 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 2 Jul 2011 17:58:23 +0200 Subject: [PATCH] Fix broken blksize negotiation, fix broken seek and change a way net device is filled n i386-pc --- grub-core/kern/file.c | 4 -- grub-core/kern/i386/pc/init.c | 8 ++-- grub-core/net/drivers/i386/pc/pxe.c | 73 +++++++++++++++++++++++------ grub-core/net/net.c | 28 +++++++---- grub-core/net/tftp.c | 26 +++++++--- include/grub/i386/pc/kernel.h | 2 + include/grub/net.h | 2 +- include/grub/net/tftp.h | 6 --- 8 files changed, 105 insertions(+), 44 deletions(-) diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c index 2407e72c5..f69ef6fd4 100644 --- a/grub-core/kern/file.c +++ b/grub-core/kern/file.c @@ -25,7 +25,6 @@ #include #include -grub_err_t (*grub_file_net_seek) (struct grub_file *file, grub_off_t offset) = NULL; void (*EXPORT_VAR (grub_grubnet_fini)) (void); grub_file_filter_t grub_file_filters_all[GRUB_FILE_FILTER_MAX]; @@ -183,9 +182,6 @@ grub_file_seek (grub_file_t file, grub_off_t offset) return -1; } - if (file->device->net && grub_file_net_seek) - grub_file_net_seek (file, offset); - old = file->offset; file->offset = offset; diff --git a/grub-core/kern/i386/pc/init.c b/grub-core/kern/i386/pc/init.c index 3aedaf6d8..24fe8fed9 100644 --- a/grub-core/kern/i386/pc/init.c +++ b/grub-core/kern/i386/pc/init.c @@ -45,9 +45,10 @@ struct mem_region static struct mem_region mem_regions[MAX_REGIONS]; static int num_regions; +void (*grub_pc_net_config) (char **device, char **path); + void -grub_machine_get_bootlocation (char **device, - char **path __attribute__ ((unused))) +grub_machine_get_bootlocation (char **device, char **path) { char *ptr; @@ -55,7 +56,8 @@ grub_machine_get_bootlocation (char **device, partition number encoded at the install time. */ if (grub_boot_drive == GRUB_BOOT_MACHINE_PXE_DL) { - *device = grub_strdup ("pxe"); + if (grub_pc_net_config) + grub_pc_net_config (device, path); return; } diff --git a/grub-core/net/drivers/i386/pc/pxe.c b/grub-core/net/drivers/i386/pc/pxe.c index 144df964c..51f4023a6 100644 --- a/grub-core/net/drivers/i386/pc/pxe.c +++ b/grub-core/net/drivers/i386/pc/pxe.c @@ -28,6 +28,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -45,6 +46,22 @@ struct grub_pxe_undi_open grub_uint8_t mcast[8][6]; } __attribute__ ((packed)); +struct grub_pxe_undi_info +{ + grub_uint16_t status; + grub_uint16_t base_io; + grub_uint16_t int_number; + grub_uint16_t mtu; + grub_uint16_t hwtype; + grub_uint16_t hwaddrlen; + grub_uint8_t current_addr[16]; + grub_uint8_t permanent_addr[16]; + grub_uint32_t romaddr; + grub_uint16_t rxbufct; + grub_uint16_t txbufct; +} __attribute__ ((packed)); + + struct grub_pxe_undi_isr { grub_uint16_t status; @@ -259,7 +276,7 @@ struct grub_net_card grub_pxe_card = void grub_pxe_unload (void) { - if (grub_pxe_pxenv) + if (pxe_rm_entry) { grub_pxe_call (GRUB_PXENV_UNDI_CLOSE, (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, @@ -269,17 +286,11 @@ grub_pxe_unload (void) } } -GRUB_MOD_INIT(pxe) +static void +grub_pc_net_config_real (char **device, char **path) { - struct grub_pxe_bangpxe *pxenv; - struct grub_pxenv_get_cached_info ci; struct grub_net_bootp_packet *bp; - struct grub_pxe_undi_open *ou; - - pxenv = grub_pxe_scan (); - if (! pxenv) - return; - + struct grub_pxenv_get_cached_info ci; ci.packet_type = GRUB_PXENV_PACKET_TYPE_DHCP_ACK; ci.buffer = 0; ci.buffer_size = 0; @@ -289,9 +300,41 @@ GRUB_MOD_INIT(pxe) bp = LINEAR (ci.buffer); - grub_memcpy (grub_pxe_card.default_address.mac, bp->mac_addr, - bp->hw_len < sizeof (grub_pxe_card.default_address.mac) - ? bp->hw_len : sizeof (grub_pxe_card.default_address.mac)); + grub_net_configure_by_dhcp_ack ("pxe", &grub_pxe_card, 0, + bp, GRUB_PXE_BOOTP_SIZE, + 1, device, path); +} + +GRUB_MOD_INIT(pxe) +{ + struct grub_pxe_bangpxe *pxenv; + struct grub_pxe_undi_open *ou; + struct grub_pxe_undi_info *ui; + unsigned i; + + pxenv = grub_pxe_scan (); + if (! pxenv) + return; + + ui = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; + grub_memset (ui, 0, sizeof (*ui)); + grub_pxe_call (GRUB_PXENV_UNDI_GET_INFORMATION, ui, pxe_rm_entry); + + grub_memcpy (grub_pxe_card.default_address.mac, ui->current_addr, + sizeof (grub_pxe_card.default_address.mac)); + for (i = 0; i < sizeof (grub_pxe_card.default_address.mac); i++) + if (grub_pxe_card.default_address.mac[i] != 0) + break; + if (i != sizeof (grub_pxe_card.default_address.mac)) + { + for (i = 0; i < sizeof (grub_pxe_card.default_address.mac); i++) + if (grub_pxe_card.default_address.mac[i] != 0xff) + break; + } + if (i == sizeof (grub_pxe_card.default_address.mac)) + grub_memcpy (grub_pxe_card.default_address.mac, ui->permanent_addr, + sizeof (grub_pxe_card.default_address.mac)); + grub_pxe_card.default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; ou = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; @@ -303,11 +346,11 @@ GRUB_MOD_INIT(pxe) return; grub_net_card_register (&grub_pxe_card); - grub_net_configure_by_dhcp_ack ("pxe", &grub_pxe_card, 0, - bp, GRUB_PXE_BOOTP_SIZE); + grub_pc_net_config = grub_pc_net_config_real; } GRUB_MOD_FINI(pxe) { + grub_pc_net_config = 0; grub_pxe_unload (); } diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 2ecb709b9..a56860eed 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -664,6 +664,7 @@ grub_net_open_real (const char *name) else ret->server = NULL; ret->fs = &grub_net_fs; + ret->offset = 0; return ret; } } @@ -784,7 +785,7 @@ grub_net_poll_cards (unsigned time) /* Read from the packets list*/ static grub_ssize_t -grub_net_fs_read (grub_file_t file, char *buf, grub_size_t len) +grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len) { grub_net_socket_t sock = file->device->net->socket; struct grub_net_buff *nb; @@ -802,6 +803,7 @@ grub_net_fs_read (grub_file_t file, char *buf, grub_size_t len) amount = len; len -= amount; total += amount; + file->device->net->offset += amount; if (buf) { grub_memcpy (ptr, nb->data, amount); @@ -829,28 +831,40 @@ grub_net_fs_read (grub_file_t file, char *buf, grub_size_t len) return total; } -/* Read from the packets list*/ static grub_err_t grub_net_seek_real (struct grub_file *file, grub_off_t offset) { grub_net_socket_t sock = file->device->net->socket; struct grub_net_buff *nb; - grub_size_t len = offset - file->offset; + grub_size_t len = offset - file->device->net->offset; if (!len) return GRUB_ERR_NONE; /* We cant seek backwards past the current packet. */ - if (file->offset > offset) + if (file->device->net->offset > offset) { nb = sock->packs.first->nb; - return grub_netbuff_push (nb, file->offset - offset); + return grub_netbuff_push (nb, file->device->net->offset - offset); } - grub_net_fs_read (file, NULL, len); + grub_net_fs_read_real (file, NULL, len); return GRUB_ERR_NONE; } +static grub_ssize_t +grub_net_fs_read (grub_file_t file, char *buf, grub_size_t len) +{ + if (file->offset != file->device->net->offset) + { + grub_err_t err; + err = grub_net_seek_real (file, file->offset); + if (err) + return err; + } + return grub_net_fs_read_real (file, buf, len); +} + static char * grub_env_write_readonly (struct grub_env_var *var __attribute__ ((unused)), const char *val __attribute__ ((unused))) @@ -1384,7 +1398,6 @@ GRUB_MOD_INIT(net) grub_fs_register (&grub_net_fs); grub_net_open = grub_net_open_real; - grub_file_net_seek = grub_net_seek_real; grub_grubnet_fini = grub_grubnet_fini_real; } @@ -1400,6 +1413,5 @@ GRUB_MOD_FINI(net) grub_unregister_command (cmd_getdhcp); grub_fs_unregister (&grub_net_fs); grub_net_open = NULL; - grub_file_net_seek = NULL; grub_grubnet_fini = NULL; } diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index 24f30eb7a..b112f643e 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -11,6 +11,13 @@ GRUB_MOD_LICENSE ("GPLv3+"); +typedef struct tftp_data +{ + grub_uint64_t file_size; + grub_uint64_t block; + grub_uint32_t block_size; +} *tftp_data_t; + static grub_err_t tftp_open (struct grub_file *file, const char *filename) { @@ -85,9 +92,9 @@ tftp_open (struct grub_file *file, const char *filename) if (file->device->net->socket->status != 0) break; /* Retry. */ - /*err = grub_net_send_udp_packet (file->device->net->socket, &nb); - if (err) - return err; */ + err = grub_net_send_udp_packet (file->device->net->socket, &nb); + if (err) + return err; } if (file->device->net->socket->status == 0) @@ -110,11 +117,11 @@ tftp_receive (grub_net_socket_t sock, struct grub_net_buff *nb) nb_ack.head = nbdata; nb_ack.end = nbdata + sizeof (nbdata); - tftph = (struct tftphdr *) nb->data; switch (grub_be_to_cpu16 (tftph->opcode)) { case TFTP_OACK: + data->block_size = 512; for (ptr = nb->data + sizeof (tftph->opcode); ptr < nb->tail;) { if (grub_memcmp (ptr, "tsize\0", sizeof ("tsize\0") - 1) == 0) @@ -122,6 +129,11 @@ tftp_receive (grub_net_socket_t sock, struct grub_net_buff *nb) data->file_size = grub_strtoul (ptr + sizeof ("tsize\0") - 1, 0, 0); } + if (grub_memcmp (ptr, "blksize\0", sizeof ("blksize\0") - 1) == 0) + { + data->block_size = grub_strtoul (ptr + sizeof ("blksize\0") - 1, + 0, 0); + } while (ptr < nb->tail && *ptr) ptr++; ptr++; @@ -139,12 +151,12 @@ tftp_receive (grub_net_socket_t sock, struct grub_net_buff *nb) { data->block++; unsigned size = nb->tail - nb->data; - if (size < 1024) + if (size < data->block_size) sock->status = 2; /* Prevent garbage in broken cards. */ - if (size > 1024) + if (size > data->block_size) { - err = grub_netbuff_unput (nb, size - 1024); + err = grub_netbuff_unput (nb, size - data->block_size); if (err) return err; } diff --git a/include/grub/i386/pc/kernel.h b/include/grub/i386/pc/kernel.h index 1de37a5d5..dd50aa833 100644 --- a/include/grub/i386/pc/kernel.h +++ b/include/grub/i386/pc/kernel.h @@ -44,6 +44,8 @@ extern grub_int32_t grub_install_bsd_part; /* The boot BIOS drive number. */ extern grub_uint8_t EXPORT_VAR(grub_boot_drive); +extern void (*EXPORT_VAR(grub_pc_net_config)) (char **device, char **path); + #endif /* ! ASM_FILE */ #endif /* ! KERNEL_MACHINE_HEADER */ diff --git a/include/grub/net.h b/include/grub/net.h index 6aaf391d5..494826625 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -242,11 +242,11 @@ typedef struct grub_net char *server; grub_net_app_level_t protocol; grub_net_socket_t socket; + grub_off_t offset; grub_fs_t fs; } *grub_net_t; extern grub_net_t (*EXPORT_VAR (grub_net_open)) (const char *name); -extern grub_err_t (*EXPORT_VAR (grub_file_net_seek)) (struct grub_file *file, grub_off_t offset); extern void (*EXPORT_VAR (grub_grubnet_fini)) (void); struct grub_net_network_level_interface diff --git a/include/grub/net/tftp.h b/include/grub/net/tftp.h index c67380817..0d8cbd1de 100644 --- a/include/grub/net/tftp.h +++ b/include/grub/net/tftp.h @@ -43,12 +43,6 @@ /* * own here because this is cleaner, and maps to the same data layout. * */ -typedef struct tftp_data - { - int file_size; - int block; - } *tftp_data_t; - struct tftphdr { grub_uint16_t opcode;