From d68f2690a91e97cc6cf977876f4b013438d09dd3 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 16 Nov 2012 20:03:50 +0000 Subject: fish --- master/jmm/efi-load-drivers-bis | 467 ++++++++ master/jmm/efi-sni-bis | 828 ++++++++++++++ master/jmm/efi_call_7-bis | 58 + master/jmm/networking-bis | 2262 +++++++++++++++++++++++++++++++++++++++ master/series | 10 + 5 files changed, 3625 insertions(+) create mode 100644 master/jmm/efi-load-drivers-bis create mode 100644 master/jmm/efi-sni-bis create mode 100644 master/jmm/efi_call_7-bis create mode 100644 master/jmm/networking-bis diff --git a/master/jmm/efi-load-drivers-bis b/master/jmm/efi-load-drivers-bis new file mode 100644 index 0000000..9cef5f7 --- /dev/null +++ b/master/jmm/efi-load-drivers-bis @@ -0,0 +1,467 @@ +diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c +index fa1d0c7..16526d0 100644 +--- a/grub-core/kern/efi/efi.c ++++ b/grub-core/kern/efi/efi.c +@@ -37,6 +37,10 @@ grub_efi_system_table_t *grub_efi_system_table; + static grub_efi_guid_t console_control_guid = GRUB_EFI_CONSOLE_CONTROL_GUID; + static grub_efi_guid_t loaded_image_guid = GRUB_EFI_LOADED_IMAGE_GUID; + static grub_efi_guid_t device_path_guid = GRUB_EFI_DEVICE_PATH_GUID; ++static grub_efi_guid_t driver_binding_guid = GRUB_EFI_DRIVER_BINDING_GUID; ++static grub_efi_guid_t driver_configuration_guid = GRUB_EFI_DRIVER_CONFIGURATION_GUID; ++static grub_efi_guid_t driver_diagnostics_guid = GRUB_EFI_DRIVER_DIAGNOSTICS_GUID; ++static grub_efi_guid_t component_name_guid = GRUB_EFI_COMPONENT_NAME_GUID; + + void * + grub_efi_locate_protocol (grub_efi_guid_t *protocol, void *registration) +@@ -740,3 +744,303 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp) + dp = (grub_efi_device_path_t *) ((char *) dp + len); + } + } ++ ++ ++grub_efi_status_t grub_efi_free_pool (void *ptr) ++{ ++ grub_efi_boot_services_t *b; ++ b = grub_efi_system_table->boot_services; ++ ++ return efi_call_1 (b->free_pool, ptr); ++} ++ ++void * grub_efi_allocate_pool (grub_efi_memory_type_t type, grub_efi_uintn_t len) ++{ ++ grub_efi_boot_services_t *b; ++ grub_efi_status_t status; ++ void *ret; ++ b = grub_efi_system_table->boot_services; ++ ++ status = efi_call_3 (b->allocate_pool, type, len, &ret); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ return (void *) 0; ++ ++ return ret; ++} ++ ++ ++static int grub_efi_guid_cmp (grub_efi_guid_t * a, grub_efi_guid_t * b) ++{ ++ return grub_memcmp (a, b, sizeof (grub_efi_guid_t)); ++} ++ ++ ++ ++grub_efi_status_t ++grub_efi_scan_handle_database (grub_efi_handle_t driver_binding_handle, ++ grub_efi_uint32_t * ++ driver_binding_i, ++ grub_efi_handle_t controller_handle, ++ grub_efi_uint32_t * controller_i, ++ grub_efi_uintn_t * handle_count, ++ grub_efi_handle_t ** handle_buffer, ++ grub_efi_uint32_t ** handle_type) ++{ ++ grub_efi_boot_services_t *b; ++ grub_efi_status_t status; ++ grub_efi_uintn_t i, j, k; ++ grub_efi_guid_t **protocol_guid_array; ++ grub_efi_uintn_t array_count; ++ grub_efi_uintn_t protocol_index; ++ grub_efi_open_protocol_information_entry_t *open_info; ++ grub_efi_uintn_t open_info_count; ++ grub_efi_boolean_t driver_binding_i_valid; ++ grub_efi_boolean_t controller_i_valid; ++ ++ b = grub_efi_system_table->boot_services; ++ ++ driver_binding_i_valid = 0; ++ if (driver_binding_i != NULL) ++ { ++ *driver_binding_i = 0xffffffff; ++ } ++ ++ controller_i_valid = 0; ++ if (controller_i != NULL) ++ { ++ *controller_i = 0xffffffff; ++ } ++ *handle_count = 0; ++ *handle_buffer = NULL; ++ *handle_type = NULL; ++ ++ // ++ // Retrieve the list of all handles from the handle database ++ // ++ status = efi_call_5 (b->locate_handle_buffer, ++ GRUB_EFI_ALL_HANDLES, ++ NULL, NULL, handle_count, handle_buffer); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ goto error; ++ ++ *handle_type = grub_malloc (*handle_count * sizeof (grub_efi_uint32_t)); ++ if (!*handle_type) ++ goto error; ++ ++ for (i = 0; i < *handle_count; i++) ++ { ++ ++ // ++ // Assume that the handle type is unknown ++ // ++ (*handle_type)[i] = GRUB_EFI_HANDLE_TYPE_UNKNOWN; ++ ++ if (driver_binding_handle != NULL && driver_binding_i != NULL ++ && (*handle_buffer)[i] == driver_binding_handle) ++ { ++ *driver_binding_i = (grub_efi_uint32_t) i; ++ driver_binding_i_valid = 1; ++ } ++ ++ if (controller_handle != NULL && controller_i != NULL ++ && (*handle_buffer)[i] == controller_handle) ++ { ++ *controller_i = (grub_efi_uint32_t) i; ++ controller_i_valid = 1; ++ } ++ } ++ ++ for (i = 0; i < *handle_count; i++) ++ { ++ ++ // ++ // Retrieve the list of all the protocols on each handle ++ // ++ status = efi_call_3 (b->protocols_per_handle, ++ (*handle_buffer)[i], ++ &protocol_guid_array, &array_count); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ continue; ++ ++ for (protocol_index = 0; protocol_index < array_count; protocol_index++) ++ { ++ ++ if (grub_efi_guid_cmp ++ (protocol_guid_array[protocol_index], &loaded_image_guid) == 0) ++ { ++ (*handle_type)[i] |= GRUB_EFI_HANDLE_TYPE_IMAGE_HANDLE; ++ } ++ ++ if (grub_efi_guid_cmp ++ (protocol_guid_array[protocol_index], ++ &driver_binding_guid) == 0) ++ { ++ (*handle_type)[i] |= GRUB_EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE; ++ } ++ ++ if (grub_efi_guid_cmp ++ (protocol_guid_array[protocol_index], ++ &driver_configuration_guid) == 0) ++ { ++ (*handle_type)[i] |= ++ GRUB_EFI_HANDLE_TYPE_DRIVER_CONFIGURATION_HANDLE; ++ } ++ ++ if (grub_efi_guid_cmp ++ (protocol_guid_array[protocol_index], ++ &driver_diagnostics_guid) == 0) ++ { ++ (*handle_type)[i] |= ++ GRUB_EFI_HANDLE_TYPE_DRIVER_DIAGNOSTICS_HANDLE; ++ } ++ ++ if (grub_efi_guid_cmp ++ (protocol_guid_array[protocol_index], ++ &component_name_guid) == 0) ++ { ++ (*handle_type)[i] |= GRUB_EFI_HANDLE_TYPE_COMPONENT_NAME_HANDLE; ++ } ++ ++ if (grub_efi_guid_cmp ++ (protocol_guid_array[protocol_index], &device_path_guid) == 0) ++ { ++ (*handle_type)[i] |= GRUB_EFI_HANDLE_TYPE_DEVICE_HANDLE; ++ } ++ ++ // ++ // Retrieve the list of agents that have opened each protocol ++ // ++ status = efi_call_4 (b->open_protocol_information, ++ (*handle_buffer)[i], ++ protocol_guid_array[protocol_index], ++ &open_info, &open_info_count); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ continue; ++ ++ for (j = 0; j < open_info_count; j++) ++ { ++ ++ if (driver_binding_handle != NULL ++ && open_info[j].agent_handle == driver_binding_handle) ++ { ++ if ((open_info[j]. ++ attributes & GRUB_EFI_OPEN_PROTOCOL_BY_DRIVER) == ++ GRUB_EFI_OPEN_PROTOCOL_BY_DRIVER) ++ { ++ // ++ // Mark the device handle as being managed by the driver specified by driver_binding_handle ++ // ++ (*handle_type)[i] |= ++ (GRUB_EFI_HANDLE_TYPE_DEVICE_HANDLE | ++ GRUB_EFI_HANDLE_TYPE_CONTROLLER_HANDLE); ++ // ++ // Mark the driver_binding_handle as being a driver that is managing at least one controller ++ // ++ if (driver_binding_i_valid) ++ { ++ (*handle_type)[*driver_binding_i] |= ++ GRUB_EFI_HANDLE_TYPE_DEVICE_DRIVER; ++ } ++ } ++ if ((open_info[j]. ++ attributes & ++ GRUB_EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) == ++ GRUB_EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) ++ { ++ // ++ // Mark the driver_binding_handle as being a driver that is managing at least one child controller ++ // ++ if (driver_binding_i_valid) ++ { ++ (*handle_type)[*driver_binding_i] |= ++ GRUB_EFI_HANDLE_TYPE_BUS_DRIVER; ++ } ++ } ++ ++ if (controller_handle != NULL ++ && (*handle_buffer)[i] == controller_handle) ++ { ++ if ((open_info[j]. ++ attributes & ++ GRUB_EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) == ++ GRUB_EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) ++ { ++ for (k = 0; k < *handle_count; k++) ++ { ++ if ((*handle_buffer)[k] == ++ open_info[j].controller_handle) ++ { ++ (*handle_type)[k] |= ++ (GRUB_EFI_HANDLE_TYPE_DEVICE_HANDLE | ++ GRUB_EFI_HANDLE_TYPE_CHILD_HANDLE); ++ } ++ } ++ } ++ } ++ } ++ ++ ++ if (driver_binding_handle == NULL ++ && open_info[j].controller_handle == controller_handle) ++ { ++ if ((open_info[j]. ++ attributes & GRUB_EFI_OPEN_PROTOCOL_BY_DRIVER) == ++ GRUB_EFI_OPEN_PROTOCOL_BY_DRIVER) ++ { ++ for (k = 0; k < *handle_count; k++) ++ { ++ if ((*handle_buffer)[k] == ++ open_info[j].agent_handle) ++ { ++ (*handle_type)[k] |= ++ GRUB_EFI_HANDLE_TYPE_DEVICE_DRIVER; ++ } ++ } ++ } ++ if ((open_info[j]. ++ attributes & ++ GRUB_EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) == ++ GRUB_EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) ++ { ++ (*handle_type)[i] |= GRUB_EFI_HANDLE_TYPE_PARENT_HANDLE; ++ for (k = 0; k < *handle_count; k++) ++ { ++ if ((*handle_buffer)[k] == ++ open_info[j].agent_handle) ++ { ++ (*handle_type)[k] |= ++ GRUB_EFI_HANDLE_TYPE_BUS_DRIVER; ++ } ++ } ++ } ++ } ++ ++ } ++ grub_efi_free_pool (open_info); ++ ++ } ++ grub_efi_free_pool (protocol_guid_array); ++ } ++ ++ return GRUB_EFI_SUCCESS; ++ ++error: ++ if (*handle_type != NULL) ++ grub_free (*handle_type); ++ ++ if (*handle_buffer != NULL) ++ grub_efi_free_pool (*handle_buffer); ++ *handle_count = 0; ++ *handle_buffer = NULL; ++ *handle_type = NULL; ++ ++ return status; ++} ++ ++ ++ ++ +diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c +index 1b0a872..a83bf2e 100644 +--- a/grub-core/kern/efi/init.c ++++ b/grub-core/kern/efi/init.c +@@ -26,6 +26,66 @@ + #include + #include + ++static void bind_all_drivers (void) ++{ ++ grub_efi_uintn_t n_all_handles; ++ grub_efi_handle_t *all_handles; ++ grub_efi_status_t status; ++ grub_efi_boot_services_t *b; ++ grub_efi_uintn_t handle_count; ++ grub_efi_handle_t *handle_buffer; ++ grub_efi_uint32_t *handle_type; ++ unsigned int i, j; ++ ++ b = grub_efi_system_table->boot_services; ++ ++ all_handles = ++ grub_efi_locate_handle (GRUB_EFI_ALL_HANDLES, NULL, NULL, &n_all_handles); ++ ++ if (!all_handles) ++ return; ++ ++ for (i = 0; i < n_all_handles; ++i) ++ { ++ int is_device = 1; ++ ++ status = ++ grub_efi_scan_handle_database (NULL, NULL, all_handles[i], NULL, ++ &handle_count, &handle_buffer, ++ &handle_type); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ continue; ++ ++ if (handle_type[i] & GRUB_EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE) ++ is_device = 0; ++ if (handle_type[i] & GRUB_EFI_HANDLE_TYPE_IMAGE_HANDLE) ++ is_device = 0; ++ ++ if (is_device) ++ { ++ int is_parent = 0; ++ for (j = 0; j < handle_count; ++j) ++ { ++ if (handle_type[j] & GRUB_EFI_HANDLE_TYPE_PARENT_HANDLE) ++ is_parent = 1; ++ } ++ ++ if ((!is_parent) ++ && (handle_type[i] & GRUB_EFI_HANDLE_TYPE_DEVICE_HANDLE)) ++ { ++ efi_call_4 (b->connect_controller, all_handles[i], NULL, NULL, 1); ++ } ++ } ++ ++ grub_free (handle_type); ++ grub_efi_free_pool (handle_buffer); ++ } ++ ++ grub_free (all_handles); ++} ++ ++ + void + grub_efi_init (void) + { +@@ -39,6 +99,8 @@ grub_efi_init (void) + efi_call_4 (grub_efi_system_table->boot_services->set_watchdog_timer, + 0, 0, 0, NULL); + ++ bind_all_drivers(); ++ + grub_efidisk_init (); + } + +diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h +index 535a3e3..d8bce06 100644 +--- a/include/grub/efi/api.h ++++ b/include/grub/efi/api.h +@@ -119,6 +119,26 @@ + { 0x82, 0x79, 0xa8, 0x4b, 0x79, 0x61, 0x78, 0x98 } \ + } + ++#define GRUB_EFI_DRIVER_BINDING_GUID \ ++ { 0x18a031ab, 0xb443, 0x4d1a, \ ++ {0xa5, 0xc0, 0xc, 0x9, 0x26, 0x1e, 0x9f, 0x71 } \ ++ } ++ ++#define GRUB_EFI_DRIVER_CONFIGURATION_GUID \ ++ { 0x107a772b, 0xd5e1, 0x11d4, \ ++ {0x9a, 0x46, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ ++ } ++ ++#define GRUB_EFI_DRIVER_DIAGNOSTICS_GUID \ ++ { 0x0784924f, 0xe296, 0x11d4, \ ++ { 0x9a, 0x49, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ ++ } ++ ++#define GRUB_EFI_COMPONENT_NAME_GUID \ ++ { 0x107a772c, 0xd5e1, 0x11d4, \ ++ {0x9a, 0x46, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ ++ } ++ + struct grub_efi_sal_system_table + { + grub_uint32_t signature; +diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h +index e9c57dd..a5b557b 100644 +--- a/include/grub/efi/efi.h ++++ b/include/grub/efi/efi.h +@@ -68,6 +68,32 @@ void grub_efi_init (void); + void grub_efi_fini (void); + void grub_efi_set_prefix (void); + ++grub_efi_status_t EXPORT_FUNC(grub_efi_scan_handle_database) ( ++ grub_efi_handle_t driver_binding_handle, ++ grub_efi_uint32_t *driver_binding_handle_index, ++ grub_efi_handle_t controller_handle, ++ grub_efi_uint32_t *controller_handle_index, ++ grub_efi_uintn_t *handle_count, ++ grub_efi_handle_t **handle_buffer, ++ grub_efi_uint32_t **handle_type); ++ ++#define GRUB_EFI_HANDLE_TYPE_UNKNOWN 0x000 ++#define GRUB_EFI_HANDLE_TYPE_IMAGE_HANDLE 0x001 ++#define GRUB_EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE 0x002 ++#define GRUB_EFI_HANDLE_TYPE_DEVICE_DRIVER 0x004 ++#define GRUB_EFI_HANDLE_TYPE_BUS_DRIVER 0x008 ++#define GRUB_EFI_HANDLE_TYPE_DRIVER_CONFIGURATION_HANDLE 0x010 ++#define GRUB_EFI_HANDLE_TYPE_DRIVER_DIAGNOSTICS_HANDLE 0x020 ++#define GRUB_EFI_HANDLE_TYPE_COMPONENT_NAME_HANDLE 0x040 ++#define GRUB_EFI_HANDLE_TYPE_DEVICE_HANDLE 0x080 ++#define GRUB_EFI_HANDLE_TYPE_PARENT_HANDLE 0x100 ++#define GRUB_EFI_HANDLE_TYPE_CONTROLLER_HANDLE 0x200 ++#define GRUB_EFI_HANDLE_TYPE_CHILD_HANDLE 0x400 ++ ++void *EXPORT_FUNC(grub_efi_allocate_pool) (grub_efi_memory_type_t type, grub_efi_uintn_t len); ++grub_efi_status_t EXPORT_FUNC(grub_efi_free_pool) (void *); ++ ++ + /* Variables. */ + extern grub_efi_system_table_t *EXPORT_VAR(grub_efi_system_table); + extern grub_efi_handle_t EXPORT_VAR(grub_efi_image_handle); diff --git a/master/jmm/efi-sni-bis b/master/jmm/efi-sni-bis new file mode 100644 index 0000000..d63ec90 --- /dev/null +++ b/master/jmm/efi-sni-bis @@ -0,0 +1,828 @@ +diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def +index 2cdcc7b..e297c4f 100644 +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -107,6 +107,7 @@ kernel = { + x86 = kern/i386/pit.c; + + x86_efi = disk/efi/efidisk.c; ++ x86_efi = net/efi/efinet.c; + x86_efi = kern/efi/efi.c; + x86_efi = kern/efi/init.c; + x86_efi = kern/efi/mm.c; +diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c +index a83bf2e..0c2a88c 100644 +--- a/grub-core/kern/efi/init.c ++++ b/grub-core/kern/efi/init.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -102,6 +103,7 @@ grub_efi_init (void) + bind_all_drivers(); + + grub_efidisk_init (); ++ grub_efinet_init (); + } + + void +@@ -133,6 +135,11 @@ grub_efi_set_prefix (void) + if (image) + { + if (!device) ++ device = grub_efinet_get_device_name (image->device_handle); ++ /* If we net booted - hard code the prefix */ ++ if (device && !path) ++ path = grub_strdup("/grub/"); ++ if (!device) + device = grub_efidisk_get_device_name (image->device_handle); + else if (device[0] == ',' || !device[0]) + { +@@ -187,6 +194,7 @@ grub_efi_set_prefix (void) + void + grub_efi_fini (void) + { ++ grub_efinet_fini (); + grub_efidisk_fini (); + grub_console_fini (); + } +diff --git a/grub-core/kern/i386/efi/init.c b/grub-core/kern/i386/efi/init.c +index f73f828..fe6971c 100644 +--- a/grub-core/kern/i386/efi/init.c ++++ b/grub-core/kern/i386/efi/init.c +@@ -30,8 +30,8 @@ + void + grub_machine_init (void) + { +- grub_efi_init (); + grub_tsc_init (); ++ grub_efi_init (); + } + + void +diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c +index 5dd5ada..0ec088e 100644 +--- a/grub-core/loader/efi/chainloader.c ++++ b/grub-core/loader/efi/chainloader.c +@@ -225,7 +225,14 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), + dp = grub_efi_get_device_path (dev_handle); + } + +- if (! dev->disk || ! dev_handle || ! dp) ++ if (dev->net) ++ { ++ dev_handle = grub_efinet_get_device_handle (dev->net); ++ if (dev_handle) ++ dp = grub_efi_get_device_path (dev_handle); ++ } ++ ++ if ((! dev->disk && ! dev->net ) || ! dev_handle || ! dp) + { + grub_error (GRUB_ERR_BAD_DEVICE, "not a valid root device"); + goto fail; +diff --git a/grub-core/net/efi/efinet.c b/grub-core/net/efi/efinet.c +new file mode 100644 +index 0000000..f26aec1 +--- /dev/null ++++ b/grub-core/net/efi/efinet.c +@@ -0,0 +1,541 @@ ++/* efinet.c - EFI network access using SNI. */ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. ++ * ++ * GRUB 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 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB 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 GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++static grub_err_t grub_efinet_start (struct grub_net *); ++ ++static grub_ether_addr ether_broadcast = ++ { {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} }; ++ ++struct grub_sni_data ++{ ++ grub_efi_handle_t handle; ++ grub_efi_device_path_t *device_path; ++ grub_efi_simple_network_t *sni; ++ struct grub_sni_data *next; ++}; ++ ++static grub_efi_guid_t sni_guid = GRUB_EFI_SIMPLE_NETWORK_PROTOCOL; ++ ++static struct grub_sni_data *sni_devices; ++ ++/* Return the device path node right before the end node. */ ++static grub_efi_device_path_t * ++find_last_device_path (const grub_efi_device_path_t * dp) ++{ ++ grub_efi_device_path_t *next, *p; ++ ++ if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp)) ++ return 0; ++ ++ for (p = (grub_efi_device_path_t *) dp, next = ++ GRUB_EFI_NEXT_DEVICE_PATH (p); !GRUB_EFI_END_ENTIRE_DEVICE_PATH (next); ++ p = next, next = GRUB_EFI_NEXT_DEVICE_PATH (next)) ++ ; ++ ++ return p; ++} ++ ++/* Compare device paths. */ ++static int ++compare_device_paths (const grub_efi_device_path_t * dp1, ++ const grub_efi_device_path_t * dp2) ++{ ++ if (!dp1 || !dp2) ++ /* Return non-zero. */ ++ return 1; ++ ++ while (1) ++ { ++ grub_efi_uint8_t type1, type2; ++ grub_efi_uint8_t subtype1, subtype2; ++ grub_efi_uint16_t len1, len2; ++ int ret; ++ ++ type1 = GRUB_EFI_DEVICE_PATH_TYPE (dp1); ++ type2 = GRUB_EFI_DEVICE_PATH_TYPE (dp2); ++ ++ if (type1 != type2) ++ return (int) type2 - (int) type1; ++ ++ subtype1 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp1); ++ subtype2 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp2); ++ ++ if (subtype1 != subtype2) ++ return (int) subtype1 - (int) subtype2; ++ ++ len1 = GRUB_EFI_DEVICE_PATH_LENGTH (dp1); ++ len2 = GRUB_EFI_DEVICE_PATH_LENGTH (dp2); ++ ++ if (len1 != len2) ++ return (int) len1 - (int) len2; ++ ++ ret = grub_memcmp (dp1, dp2, len1); ++ if (ret != 0) ++ return ret; ++ ++ if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp1)) ++ break; ++ ++ dp1 = (grub_efi_device_path_t *) ((char *) dp1 + len1); ++ dp2 = (grub_efi_device_path_t *) ((char *) dp2 + len2); ++ } ++ ++ return 0; ++} ++ ++ ++ ++static void ++free_devices (struct grub_sni_data *devices) ++{ ++ struct grub_sni_data *p, *q; ++ ++ for (p = devices; p; p = q) ++ { ++ q = p->next; ++ grub_free (p); ++ } ++} ++ ++ ++/* Add a device into a list of devices in an ascending order. */ ++static void ++add_device (struct grub_sni_data *d) ++{ ++ struct grub_sni_data **p; ++ ++ ++ for (p = &sni_devices; *p; p = &((*p)->next)) ++ { ++ int ret; ++ ++ ret = compare_device_paths (find_last_device_path ((*p)->device_path), ++ find_last_device_path (d->device_path)); ++ if (ret == 0) ++ ret = compare_device_paths ((*p)->device_path, d->device_path); ++ if (ret == 0) ++ return; ++ else if (ret > 0) ++ break; ++ } ++ ++ ++ d->next = (*p); ++ (*p) = d; ++} ++ ++ ++ ++static void ++make_devices (void) ++{ ++ grub_efi_uintn_t num_handles; ++ grub_efi_handle_t *handles; ++ grub_efi_handle_t *handle; ++ ++ handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &sni_guid, ++ 0, &num_handles); ++ ++ if (!handles) ++ return; ++ ++ ++ ++ for (handle = handles; num_handles--; handle++) ++ { ++ grub_efi_simple_network_t *sni; ++ grub_efi_device_path_t *dp; ++ struct grub_sni_data *d; ++ ++ ++ dp = grub_efi_get_device_path (*handle); ++ ++ sni = grub_efi_open_protocol (*handle, &sni_guid, ++ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); ++ ++ if (!sni) ++ continue; ++ ++ ++ d = grub_malloc (sizeof (*d)); ++ if (!d) ++ { ++ grub_free (handles); ++ return; ++ } ++ ++ d->handle = *handle; ++ d->device_path = dp; ++ d->sni = sni; ++ ++ add_device (d); ++ ++ } ++ ++ ++ grub_free (handles); ++ ++} ++static void ++enumerate_snis (void) ++{ ++ make_devices (); ++} ++ ++ ++static int ++get_unit_number (const char *name) ++{ ++ unsigned long unit; ++ ++ if (name[0] != 's' || name[1] != 'n' || name[2] != 'i') ++ goto fail; ++ ++ unit = grub_strtoul (name + 3, 0, 10); ++ if (grub_errno != GRUB_ERR_NONE) ++ goto fail; ++ ++ return (int) unit; ++ ++fail: ++ grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a sni"); ++ return -1; ++} ++ ++static struct grub_sni_data * ++get_device (int num) ++{ ++ struct grub_sni_data *d; ++ ++ for (d = sni_devices; d && num; d = d->next, num--); ++ ++ if (num == 0) ++ return d; ++ ++ return 0; ++} ++ ++ ++static int ++tx_worker (struct grub_net *net, const void *frame, int len) ++{ ++ struct grub_sni_data *private = (struct grub_sni_data *) net->data; ++ grub_uint64_t current_time, tx_time; ++ void *buf = NULL; ++ grub_efi_status_t status; ++ ++ static int need_bodge = 1; ++ ++ /* GRR the beasty EFI on the 64bit macminis doesn't return */ ++ /* addresses from sni->transmit in sni->get_status, so we have to bodge */ ++ /* the obvious bodge of looking at tx_good_frames in ->statistics */ ++ /* doesn't work as that call appears to be missing on macs, the alternative */ ++ /* is to flush the transmit q before and return whenever any buffer is */ ++ /* returned. */ ++ ++ /* Flush list of frames */ ++ do ++ { ++ buf = NULL; ++ efi_call_3 (private->sni->get_status, private->sni, NULL, ++ (void **) &buf); ++ } ++ while (buf); ++ ++ ++ tx_time = grub_get_time_ms (); ++ ++ status = ++ efi_call_7 (private->sni->transmit, private->sni, 0, len, frame, NULL, ++ NULL, NULL); ++ ++ if (status == GRUB_EFI_NOT_READY) ++ return 0; /*Try again */ ++ ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ grub_efinet_start (net); /*Reset the interface */ ++ return 0; /*Try again */ ++ } ++ ++ do ++ { ++ current_time = grub_get_time_ms (); ++ buf = NULL; ++ status = ++ efi_call_3 (private->sni->get_status, private->sni, NULL, ++ (void **) &buf); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ continue; ++ ++ if (buf == frame) ++ { ++ /*EFI did TRT we can return success */ ++ need_bodge = 0; ++ return len; ++ } ++ ++ if (need_bodge) ++ return len; ++ ++ } ++ while ((current_time - tx_time) < 1000); ++ ++ /*Tx timed out reset and try again */ ++ grub_efinet_start (net); /*Reset the interface */ ++ return 0; /*Try again */ ++ ++} ++ ++ ++ ++static grub_err_t ++grub_efinet_open (const char *name, struct grub_net *net) ++{ ++ int num; ++ struct grub_sni_data *d = NULL; ++ ++ ++ if (name) ++ { ++ num = get_unit_number (name); ++ if (num < 0) ++ return grub_errno; ++ net->name = grub_strdup (name); ++ } ++ else ++ { ++ num = 0; ++ net->name = grub_strdup ("sni0"); ++ } ++ ++ d = get_device (num); ++ ++ if (!d) ++ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such device"); ++ ++ ++ grub_printf("DEVICE PATH to %s:",name); ++ grub_efi_print_device_path(d->device_path); ++ grub_printf("\n"); ++ ++//net->id=num; ++ net->data = d; ++ ++ return GRUB_ERR_NONE; ++} ++ ++ ++static grub_err_t ++grub_efinet_start (struct grub_net *net) ++{ ++ struct grub_sni_data *private = (struct grub_sni_data *) net->data; ++ grub_efi_status_t status; ++ ++ int tries = 3; ++ ++ efi_call_1 (private->sni->shutdown, private->sni); ++ status = efi_call_3 (private->sni->initialize, private->sni, 0, 0); ++ ++ ++#if 0 ++ efi_call_1 (private->sni->shutdown, private->sni); ++ efi_call_1 (private->sni->stop, private->sni); ++#endif ++ ++ do ++ { ++ status = efi_call_1 (private->sni->start, private->sni); ++ if (status != GRUB_EFI_SUCCESS) ++ { ++ efi_call_1 (private->sni->shutdown, private->sni); ++ efi_call_1 (private->sni->stop, private->sni); ++ } ++ } ++ while ((status != GRUB_EFI_SUCCESS) && (tries--)); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ return -1; ++ ++ status = efi_call_3 (private->sni->initialize, private->sni, 0, 0); ++ if (status != GRUB_EFI_SUCCESS) ++ goto fail; ++ ++ status = ++ efi_call_6 (private->sni->receive_filters, private->sni, ++ GRUB_EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | ++ GRUB_EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST, 0, 0, 0, NULL); ++ if (status != GRUB_EFI_SUCCESS) ++ goto fail; ++ ++ if (private->sni->mode->state != grub_efi_simple_network_initialized) ++ goto fail; ++ ++ if (private->sni->mode->hw_address_size != GRUB_ETH_ALEN) ++ goto fail; ++ ++ grub_memcpy (&net->my_eth_addr, private->sni->mode->current_address, ++ GRUB_ETH_ALEN); ++ ++ net->is_running++; ++ ++ return 0; ++ ++fail: ++ ++ efi_call_1 (private->sni->shutdown, private->sni); ++ efi_call_1 (private->sni->stop, private->sni); ++ return -1; ++} ++ ++static void ++grub_efinet_stop (struct grub_net *net) ++{ ++ struct grub_sni_data *private = (struct grub_sni_data *) net->data; ++ ++ efi_call_1 (private->sni->shutdown, private->sni); ++ efi_call_1 (private->sni->stop, private->sni); ++ net->is_running = 0; ++} ++ ++ ++static void ++grub_efinet_close (struct grub_net *net) ++{ ++ grub_efinet_stop (net); ++ grub_free (net->name); ++} ++ ++ ++static grub_err_t ++grub_efinet_send (struct grub_net *net, const void *frame, int len) ++{ ++ int tries = 3; ++ int ret; ++ ++ if (len <= 0) ++ return -1; ++ ++ do ++ { ++ ret = tx_worker (net, frame, len); ++ if (ret > 0) ++ break; ++ } ++ while (tries--); ++ ++ ++ /* vodoo here - efi seems to drop packets in the TxQ for a short */ ++ /* while after sending a broadcast packet - god alone knows why */ ++ ++ if (!grub_memcmp (frame, ðer_broadcast, GRUB_ETH_ALEN)) ++ { ++ efi_call_1 (grub_efi_system_table->boot_services->stall, 100000); ++ } ++ ++ return (ret > 0) ? GRUB_ERR_NONE : GRUB_ERR_WRITE_ERROR; ++ ++} ++ ++ ++static grub_err_t ++grub_efinet_recv (struct grub_net *net, void *frame, int *lenp) ++{ ++ struct grub_sni_data *private = (struct grub_sni_data *) net->data; ++ grub_efi_status_t st; ++ grub_efi_uintn_t len = GRUB_ETH_FRAME_LEN; ++ ++ st = ++ efi_call_7 (private->sni->receive, private->sni, NULL, &len, frame, NULL, ++ NULL, NULL); ++ ++ *lenp = len; ++ ++ ++ if (st == GRUB_EFI_NOT_READY) ++ return GRUB_ERR_TIMEOUT; ++ else if (st != GRUB_EFI_SUCCESS) ++ return GRUB_ERR_READ_ERROR; ++ ++ ++ return GRUB_ERR_NONE; ++} ++ ++static struct grub_net_dev sni_net_dev = { ++ .name = "sni", ++ .open = grub_efinet_open, ++ .start = grub_efinet_start, ++ .stop = grub_efinet_stop, ++ .close = grub_efinet_close, ++ .send = grub_efinet_send, ++ .recv = grub_efinet_recv, ++ .next = 0 ++}; ++ ++grub_efi_handle_t ++grub_net_efinet_get_device_handle(struct grub_net *net) ++{ ++ struct grub_sni_data *private = (struct grub_sni_data *) net->data; ++ return private->handle; ++} ++ ++void ++grub_net_efinet_init (void) ++{ ++ enumerate_snis (); ++ grub_net_dev_register (&sni_net_dev); ++} ++ ++void ++grub_net_efinet_fini (void) ++{ ++ grub_net_dev_unregister (&sni_net_dev); ++ free_devices (sni_devices); ++} ++ ++char * grub_net_efinet_get_device_name(grub_efi_handle_t h) ++{ ++ struct grub_sni_data *p; ++ int i=0; ++ ++ while (p=get_device(i)) { ++ if (p && (p->handle == h)) { ++ char *ret=grub_malloc (10); //FIXME ++ grub_sprintf(ret,"sni%d",i); ++ return ret; ++ } ++ i++; ++ } ++ ++ return NULL; ++} +diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h +index db63eec..a965d60 100644 +--- a/include/grub/efi/api.h ++++ b/include/grub/efi/api.h +@@ -232,6 +232,12 @@ enum + GRUB_EFI_SAL_SYSTEM_TABLE_PLATFORM_FEATURE_ITCDRIFT = 8, + }; + ++#define GRUB_EFI_SIMPLE_NETWORK_PROTOCOL \ ++ { 0xA19832B9, 0xAC25, 0x11D3, \ ++ {0x9A, 0x2D, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D} \ ++ } ++ ++ + /* Enumerations. */ + enum grub_efi_timer_delay + { +@@ -1254,6 +1260,138 @@ struct grub_efi_block_io + }; + typedef struct grub_efi_block_io grub_efi_block_io_t; + ++ ++struct grub_efi_network_statistics { ++ grub_efi_uint64_t rx_total_frames; ++ grub_efi_uint64_t rx_good_frames; ++ grub_efi_uint64_t rx_undersize_frames; ++ grub_efi_uint64_t rx_oversize_frames; ++ grub_efi_uint64_t rx_dropped_frames; ++ grub_efi_uint64_t rx_unicast_frames; ++ grub_efi_uint64_t rx_broadcast_frames; ++ grub_efi_uint64_t rx_multicast_frames; ++ grub_efi_uint64_t rx_crc_error_frames; ++ grub_efi_uint64_t rx_total_bytes; ++ ++ grub_efi_uint64_t tx_total_frames; ++ grub_efi_uint64_t tx_good_frames; ++ grub_efi_uint64_t tx_undersize_frames; ++ grub_efi_uint64_t tx_oversize_frames; ++ grub_efi_uint64_t tx_dropped_frames; ++ grub_efi_uint64_t tx_unicast_frames; ++ grub_efi_uint64_t tx_broadcast_frames; ++ grub_efi_uint64_t tx_multicast_frames; ++ grub_efi_uint64_t tx_crc_error_frames; ++ grub_efi_uint64_t tx_total_bytes; ++ ++ grub_efi_uint64_t collisions; ++ grub_efi_uint64_t unsupported_protocol; ++}; ++typedef struct grub_efi_network_statistics grub_efi_network_statistics_t; ++ ++enum grub_efi_simple_network_state { ++ grub_efi_simple_network_stopped, ++ grub_efi_simple_network_started, ++ grub_efi_simple_network_initialized, ++ grub_efi_simple_network_max_state ++}; ++typedef enum grub_efi_simple_network_state grub_efi_simple_network_state_t; ++ ++#define GRUB_EFI_SIMPLE_NETWORK_RECEIVE_UNICAST 0x01 ++#define GRUB_EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST 0x02 ++#define GRUB_EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST 0x04 ++#define GRUB_EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS 0x08 ++#define GRUB_EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST 0x10 ++ ++ ++#define GRUB_EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT 0x01 ++#define GRUB_EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT 0x02 ++#define GRUB_EFI_SIMPLE_NETWORK_COMMAND_INTERRUPT 0x04 ++#define GRUB_EFI_SIMPLE_NETWORK_SOFTWARE_INTERRUPT 0x08 ++ ++#define GRUB_EFI_MAX_MCAST_FILTER_CNT 16 ++struct grub_efi_simple_network_mode ++{ ++ grub_efi_uint32_t state; ++ grub_efi_uint32_t hw_address_size; ++ grub_efi_uint32_t media_header_size; ++ grub_efi_uint32_t max_packet_size; ++ grub_efi_uint32_t nv_ram_size; ++ grub_efi_uint32_t nv_ram_access_size; ++ grub_efi_uint32_t receive_filter_mask; ++ grub_efi_uint32_t receive_filter_setting; ++ grub_efi_uint32_t max_mcast_filter_count; ++ grub_efi_uint32_t mcast_filter_count; ++ grub_efi_mac_address_t mcast_filter[GRUB_EFI_MAX_MCAST_FILTER_CNT]; ++ grub_efi_mac_address_t current_address; ++ grub_efi_mac_address_t broadcast_address; ++ grub_efi_mac_address_t permanent_address; ++ grub_efi_uint8_t if_type; ++ grub_efi_boolean_t mac_address_changeable; ++ grub_efi_boolean_t multiple_tx_supported; ++ grub_efi_boolean_t media_present_supported; ++ grub_efi_boolean_t media_present; ++}; ++typedef struct grub_efi_simple_network_mode grub_efi_simple_network_mode_t; ++ ++ ++struct grub_efi_simple_network ++{ ++ grub_efi_uint64_t revision; ++ grub_efi_status_t (*start) (struct grub_efi_simple_network *this); ++ grub_efi_status_t (*stop) (struct grub_efi_simple_network *this); ++ grub_efi_status_t (*initialize) (struct grub_efi_simple_network *this, ++ grub_efi_uintn_t extra_rx_buffer_size, ++ grub_efi_uintn_t extra_tx_buffer_size); ++ grub_efi_status_t (*reset) (struct grub_efi_simple_network *this, ++ grub_efi_boolean_t extended_verification); ++ grub_efi_status_t (*shutdown) (struct grub_efi_simple_network *this); ++ grub_efi_status_t (*receive_filters) (struct grub_efi_simple_network *this, ++ grub_efi_uint32_t enable, ++ grub_efi_uint32_t disable, ++ grub_efi_boolean_t reset_mcast_filter, ++ grub_efi_uintn_t mcast_filter_count, ++ grub_efi_mac_address_t *mcast_filter); ++ grub_efi_status_t (*station_address) (struct grub_efi_simple_network *this, ++ grub_efi_boolean_t reset, ++ grub_efi_mac_address_t *new); ++ grub_efi_status_t (*statistics) (struct grub_efi_simple_network *this, ++ grub_efi_boolean_t reset, ++ grub_efi_uintn_t *statistics_size, ++ grub_efi_network_statistics_t *statistics_table); ++ grub_efi_status_t (*mcast_ip_to_mac) (struct grub_efi_simple_network *this, ++ grub_efi_boolean_t ipv6, ++ grub_efi_ip_address_t *ip, ++ grub_efi_mac_address_t *mac); ++ grub_efi_status_t (*nvdata) (struct grub_efi_simple_network *this, ++ grub_efi_boolean_t read_write, ++ grub_efi_uintn_t offset, ++ grub_efi_uintn_t buffer_size, ++ void *buffer); ++ grub_efi_status_t (*get_status) (struct grub_efi_simple_network *this, ++ grub_efi_uint32_t *interrupt_status, ++ void **tx_buf); ++ grub_efi_status_t (*transmit) (struct grub_efi_simple_network *this, ++ grub_efi_uintn_t header_size, ++ grub_efi_uintn_t buffer_size, ++ void *buffer, ++ grub_efi_mac_address_t *src_addr, ++ grub_efi_mac_address_t *dst_addr, ++ grub_efi_uint16_t *protocol); ++ grub_efi_status_t (*receive) (struct grub_efi_simple_network *this, ++ grub_efi_uintn_t *header_size, ++ grub_efi_uintn_t *buffer_size, ++ void *buffer, ++ grub_efi_mac_address_t *src_addr, ++ grub_efi_mac_address_t *dst_addr, ++ grub_efi_uint16_t *protocol); ++ grub_efi_status_t (*wait_for_packet) (grub_efi_event_t event); ++ grub_efi_simple_network_mode_t *mode; ++}; ++typedef struct grub_efi_simple_network grub_efi_simple_network_t; ++ ++ ++ + #if GRUB_TARGET_SIZEOF_VOID_P == 4 + + #define efi_call_0(func) func() +diff --git a/include/grub/efi/net.h b/include/grub/efi/net.h +new file mode 100644 +index 0000000..368eaab +--- /dev/null ++++ b/include/grub/efi/net.h +@@ -0,0 +1,33 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2006,2007 Free Software Foundation, Inc. ++ * ++ * GRUB 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 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB 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 GRUB. If not, see . ++ */ ++ ++#ifndef GRUB_EFI_NET_HEADER ++#define GRUB_EFI_NET_HEADER 1 ++ ++#include ++#include ++#include ++ ++grub_efi_handle_t ++EXPORT_FUNC(grub_efinet_get_device_handle) (grub_net_t net); ++char *EXPORT_FUNC(grub_efinet_get_device_name) (grub_efi_handle_t *handle); ++ ++void grub_efinet_init (void); ++void grub_efinet_fini (void); ++ ++#endif /* ! GRUB_EFI_NET_HEADER */ diff --git a/master/jmm/efi_call_7-bis b/master/jmm/efi_call_7-bis new file mode 100644 index 0000000..e41e0a3 --- /dev/null +++ b/master/jmm/efi_call_7-bis @@ -0,0 +1,58 @@ +diff --git a/grub-core/kern/x86_64/efi/callwrap.S b/grub-core/kern/x86_64/efi/callwrap.S +index aae2678..36df0a6 100644 +--- a/grub-core/kern/x86_64/efi/callwrap.S ++++ b/grub-core/kern/x86_64/efi/callwrap.S +@@ -95,6 +95,20 @@ FUNCTION(efi_wrap_6) + addq $64, %rsp + ret + ++FUNCTION(efi_wrap_7) ++ subq $68, %rsp ++ mov 68+16(%rsp), %rax ++ mov %rax, 48(%rsp) ++ mov 68+8(%rsp), %rax ++ mov %rax, 40(%rsp) ++ mov %r9, 32(%rsp) ++ mov %r8, %r9 ++ mov %rcx, %r8 ++ mov %rsi, %rcx ++ call *%rdi ++ addq $68, %rsp ++ ret ++ + FUNCTION(efi_wrap_10) + subq $96, %rsp + mov 96+40(%rsp), %rax +diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h +index cb6b111..6c17bdb 100644 +--- a/include/grub/efi/api.h ++++ b/include/grub/efi/api.h +@@ -1243,6 +1243,7 @@ typedef struct grub_efi_block_io grub_efi_block_io_t; + #define efi_call_4(func, a, b, c, d) func(a, b, c, d) + #define efi_call_5(func, a, b, c, d, e) func(a, b, c, d, e) + #define efi_call_6(func, a, b, c, d, e, f) func(a, b, c, d, e, f) ++#define efi_call_7(func, a, b, c, d, e, f, g) func(a, b, c, d, e, f, g) + #define efi_call_10(func, a, b, c, d, e, f, g, h, i, j) func(a, b, c, d, e, f, g, h, i, j) + + #else +@@ -1264,6 +1265,9 @@ typedef struct grub_efi_block_io grub_efi_block_io_t; + #define efi_call_6(func, a, b, c, d, e, f) \ + efi_wrap_6(func, (grub_uint64_t) a, (grub_uint64_t) b, (grub_uint64_t) c, \ + (grub_uint64_t) d, (grub_uint64_t) e, (grub_uint64_t) f) ++#define efi_call_7(func, a, b, c, d, e, f, g) \ ++ efi_wrap_7(func, (grub_uint64_t) a, (grub_uint64_t) b, (grub_uint64_t) c, \ ++ (grub_uint64_t) d, (grub_uint64_t) e, (grub_uint64_t) f, (grub_uint64_t) g) + #define efi_call_10(func, a, b, c, d, e, f, g, h, i, j) \ + efi_wrap_10(func, (grub_uint64_t) a, (grub_uint64_t) b, (grub_uint64_t) c, \ + (grub_uint64_t) d, (grub_uint64_t) e, (grub_uint64_t) f, (grub_uint64_t) g, \ +@@ -1285,6 +1289,10 @@ grub_uint64_t EXPORT_FUNC(efi_wrap_6) (void *func, grub_uint64_t arg1, + grub_uint64_t arg2, grub_uint64_t arg3, + grub_uint64_t arg4, grub_uint64_t arg5, + grub_uint64_t arg6); ++grub_uint64_t EXPORT_FUNC(efi_wrap_7) (void *func, grub_uint64_t arg1, ++ grub_uint64_t arg2, grub_uint64_t arg3, ++ grub_uint64_t arg4, grub_uint64_t arg5, ++ grub_uint64_t arg6, grub_uint64_t arg7); + grub_uint64_t EXPORT_FUNC(efi_wrap_10) (void *func, grub_uint64_t arg1, + grub_uint64_t arg2, grub_uint64_t arg3, + grub_uint64_t arg4, grub_uint64_t arg5, diff --git a/master/jmm/networking-bis b/master/jmm/networking-bis new file mode 100644 index 0000000..2752895 --- /dev/null +++ b/master/jmm/networking-bis @@ -0,0 +1,2262 @@ +diff --git a/Makefile.util.def b/Makefile.util.def +index 093f448..a49707e 100644 +--- a/Makefile.util.def ++++ b/Makefile.util.def +@@ -9,6 +9,7 @@ library = { + common = grub-core/kern/command.c; + common = grub-core/kern/device.c; + common = grub-core/kern/disk.c; ++ common = grub-core/kern/net.c; + common = grub-core/kern/emu/getroot.c; + common = grub-core/kern/emu/hostdisk.c; + common = grub-core/kern/emu/misc.c; +diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am +index 94f7f3f..0012e59 100644 +--- a/grub-core/Makefile.am ++++ b/grub-core/Makefile.am +@@ -61,6 +61,7 @@ include $(srcdir)/Makefile.core.am + KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/cache.h + KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/command.h + KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/device.h ++KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/net.h + KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/disk.h + KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/dl.h + KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/env.h +diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def +index 6979369..2cdcc7b 100644 +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -56,6 +56,7 @@ kernel = { + common = kern/corecmd.c; + common = kern/device.c; + common = kern/disk.c; ++ common = kern/net.c; + common = kern/dl.c; + common = kern/env.c; + common = kern/err.c; +@@ -948,6 +949,11 @@ module = { + }; + + module = { ++ name = tftp; ++ common = fs/tftp.c; ++}; ++ ++module = { + name = jfs; + common = fs/jfs.c; + }; +diff --git a/grub-core/commands/boot.c b/grub-core/commands/boot.c +index 7714011..c3dbe12 100644 +--- a/grub-core/commands/boot.c ++++ b/grub-core/commands/boot.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -148,6 +149,8 @@ grub_loader_boot (void) + if (! grub_loader_loaded) + return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel"); + ++ grub_net_shutdown(); ++ + if (grub_loader_noreturn) + grub_machine_fini (); + +diff --git a/grub-core/fs/tftp.c b/grub-core/fs/tftp.c +new file mode 100644 +index 0000000..941c907 +--- /dev/null ++++ b/grub-core/fs/tftp.c +@@ -0,0 +1,504 @@ ++/* tftp.c - tftp filesystem */ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2002,2005,2007 Free Software Foundation, Inc. ++ * ++ * GRUB 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 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB 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 GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#define TFTPS_PORT 69 ++ ++#define TFTP_BLOCK_SIZE 512 ++ ++#define TFTP_OP_RRQ 1 ++#define TFTP_OP_WRQ 2 ++#define TFTP_OP_DATA 3 ++#define TFTP_OP_ACK 4 ++#define TFTP_OP_ERROR 5 ++ ++static grub_dl_t my_mod; ++ ++typedef struct __attribute__ ((packed)) ++{ ++ grub_uint16_t op; ++ grub_uint8_t data[]; ++} tftp_hdr; ++ ++typedef struct __attribute__ ((packed)) ++{ ++ grub_uint16_t op; ++ grub_uint16_t block; ++ grub_uint8_t data[0]; ++} tftp_ackdata; ++ ++typedef struct __attribute__ ((packed)) ++{ ++ grub_uint16_t op; ++ grub_uint16_t errorcode; ++ grub_uint16_t errmsg[0]; ++} tftp_err; ++ ++ ++#define TFTP_TIMEOUT 2000 ++#define TFTP_TRIES 3 ++ ++enum tftp_state ++{ ++ TFTP_STATE_IDLE = 0, ++ TFTP_STATE_SENT_REQ, ++ TFTP_STATE_DATA, ++ TFTP_STATE_TIMEDOUT, ++ TFTP_STATE_ERROR, ++ TFTP_STATE_DONE ++}; ++typedef enum tftp_state tftp_state_t; ++ ++struct tftp_file_data ++{ ++ char path[1024]; ++ tftp_state_t state; ++ grub_udp_socket_t *socket; ++ grub_net_t net; ++ ++ ++ grub_uint64_t timeout; ++ ++ int length; ++ int buf_size; ++ grub_uint8_t *buf; ++}; ++typedef struct tftp_file_data tftp_file_data_t; ++ ++ ++static void ++ack (grub_net_t net, grub_udp_socket_t * socket, int block, ++ grub_ipv4_addr * src, grub_uint16_t sport) ++{ ++ tftp_ackdata ta; ++ ++ ta.op = grub_net_htons (TFTP_OP_ACK); ++ ta.block = grub_net_htons (block); ++ ++ grub_udp_send (net, socket->port, (grub_uint8_t *) & ta, sizeof (ta), src, ++ sport); ++} ++ ++ ++ ++static void ++data (tftp_file_data_t * fd, grub_net_t net, grub_udp_socket_t * socket, ++ grub_uint8_t * packet, int len, grub_ipv4_addr * src, ++ grub_uint16_t sport) ++{ ++ tftp_ackdata *td; ++ int block; ++ int offset; ++ ++ if ((fd->state != TFTP_STATE_DATA) && (fd->state != TFTP_STATE_SENT_REQ)) ++ return; ++ ++ if (len < (int) sizeof (tftp_ackdata)) ++ return; ++ ++ fd->state = TFTP_STATE_DATA; ++ ++ td = (tftp_ackdata *) packet; ++ len -= sizeof (tftp_ackdata); ++ ++ block = grub_net_ntohs (td->block); ++ ++ offset = (block - 1) * TFTP_BLOCK_SIZE; ++ ++ if ((offset + len) > fd->length) ++ fd->length = offset + len; ++ ++ if (len) ++ { ++ ++ if (fd->length > fd->buf_size) ++ { ++ while (fd->length > fd->buf_size) ++ fd->buf_size <<= 1; ++ ++ fd->buf = grub_realloc (fd->buf, fd->buf_size); ++ ++ } ++ if (!fd->buf) ++ { ++ grub_printf ("failed to realloc %d bytes\n", fd->buf_size); ++ grub_millisleep (5000); ++ } ++ ++ if (!fd->buf) ++ { ++ fd->state = TFTP_STATE_ERROR; ++ } ++ else ++ { ++ grub_memcpy (&fd->buf[offset], td->data, len); ++ } ++ } ++ ++ fd->timeout = grub_get_time_ms () + TFTP_TIMEOUT; ++ ++ if (len != TFTP_BLOCK_SIZE) ++ { ++ fd->state = TFTP_STATE_DONE; ++ } ++ ++ ack (net, socket, block, src, sport); ++} ++ ++static void ++error (tftp_file_data_t * fd) ++{ ++ fd->state = TFTP_STATE_ERROR; ++} ++ ++ ++ ++static void ++rrq (tftp_file_data_t * fd) ++{ ++ grub_uint8_t frame[GRUB_UDP_DATA_LEN]; ++ int len = 0; ++ char *fn = fd->path; ++ ++ tftp_hdr *t = (tftp_hdr *) frame; ++ grub_uint8_t *ptr; ++ ++ t->op = grub_net_htons (TFTP_OP_RRQ); ++ ptr = t->data; ++ len += sizeof (tftp_hdr); ++ ++ while (*fn) ++ { ++ *(ptr++) = *(fn++); ++ len++; ++ } ++ ++ *(ptr++) = 0; ++ *(ptr++) = 'o'; ++ *(ptr++) = 'c'; ++ *(ptr++) = 't'; ++ *(ptr++) = 'e'; ++ *(ptr++) = 't'; ++ *(ptr++) = 0; ++ len += 7; ++ ++ grub_udp_send (fd->net, fd->socket->port, frame, len, &fd->net->tftp_addr, ++ TFTPS_PORT); ++ ++ fd->state = TFTP_STATE_SENT_REQ; ++} ++ ++ ++static void ++tftp_new_frame (grub_udp_socket_t * socket, grub_net_t net, ++ grub_uint8_t * msg, int len, grub_ipv4_addr * src, ++ grub_uint16_t src_port) ++{ ++ tftp_file_data_t *fd = (tftp_file_data_t *) socket->data; ++ tftp_hdr *t; ++ ++ if (grub_memcmp (src, &fd->net->tftp_addr, GRUB_IPV4_ALEN)) ++ return; ++ if (len < (int) sizeof (tftp_hdr)) ++ return; ++ ++ t = (tftp_hdr *) msg; ++ ++ switch (grub_net_ntohs (t->op)) ++ { ++ case TFTP_OP_DATA: ++ data (fd, net, socket, msg, len, src, src_port); ++ break; ++ case TFTP_OP_ERROR: ++ error (fd); ++ break; ++ } ++} ++ ++static int ++tftp (tftp_file_data_t * fd) ++{ ++ int tries = 0; ++ ++ fd->state = TFTP_STATE_IDLE; ++ ++ fd->length = 0; ++ fd->buf_size = 1024; ++ fd->buf = grub_malloc (fd->buf_size); ++ if (!fd->buf) ++ goto fail; ++ ++ fd->socket = grub_malloc (sizeof (struct grub_udp_socket)); ++ if (!fd->socket) ++ goto fail; ++ ++ ++ fd->socket->data = fd; ++ fd->socket->new_frame = tftp_new_frame; ++ ++ do ++ { ++ fd->socket->port = 0; /* We want to be given a different port for each attempt */ ++ ++ if (tries) ++ grub_sleep (1); /* Back off */ ++ ++ if (grub_udp_bind (fd->socket)) ++ goto fail; ++ ++ fd->timeout = grub_get_time_ms () + TFTP_TIMEOUT; ++ ++ rrq (fd); ++ ++ while ((grub_get_time_ms () < fd->timeout) ++ && (fd->state == TFTP_STATE_SENT_REQ)) ++ grub_net_dispatch (); ++ ++ if (fd->state == TFTP_STATE_SENT_REQ) ++ grub_udp_unbind (fd->socket); ++ ++ } ++ while ((fd->state == TFTP_STATE_SENT_REQ) && (tries++ < TFTP_TRIES)); ++ ++ ++ if (fd->state == TFTP_STATE_SENT_REQ) ++ { ++ grub_printf ("tftp - timeout after rrq\n"); ++ //TIMEOUT ++ goto fail; ++ } ++ ++ fd->timeout = grub_get_time_ms () + TFTP_TIMEOUT; ++ ++ while ((grub_get_time_ms () < fd->timeout) ++ && (fd->state == TFTP_STATE_DATA)) ++ grub_net_dispatch (); ++ ++ ++ ++ if (fd->state == TFTP_STATE_DONE) ++ { ++ grub_dprintf ("tftp","tftp - success\n"); ++ } ++ else if (fd->state == TFTP_STATE_ERROR) ++ { ++ grub_dprintf ("tftp","tftp - remote reported error\n"); ++ } ++ else if (fd->state == TFTP_STATE_DATA) ++ { ++ grub_printf ("tftp - timeout in data phase\n"); ++ } ++ ++fail: ++ ++ if (fd->socket) ++ { ++ grub_udp_unbind (fd->socket); ++ grub_free (fd->socket); ++ } ++ fd->socket = NULL; ++ ++ return (fd->state == TFTP_STATE_DONE) ? fd->length : -1; ++ ++} ++ ++static void ++ip_to_x (grub_ipv4_addr * s, char *d) ++{ ++ int i, j; ++ ++ for (i = 0; i < 4; ++i) ++ { ++ for (j = 4; j >= 0; j -= 4) ++ { ++ int k = (s->s_addr[i] >> j) & 0xf; ++ if (k < 0xA) ++ *(d++) = '0' + k; ++ else ++ *(d++) = 'A' + (k - 0xA); ++ } ++ } ++ *(d++) = 0; ++} ++ ++ ++static grub_err_t ++grub_tftp_open (grub_file_t file, const char *name) ++{ ++ char ipx[9]; ++ char *p; ++ tftp_file_data_t *fd; ++ int i; ++ int j; ++ int ret; ++ ++ grub_dl_ref (my_mod); ++ ++ grub_dprintf ("tftp","tftp_open_called\n"); ++ ++ fd = grub_malloc (sizeof (*fd)); ++ if (!fd) ++ goto fail; ++ file->data = fd; ++ ++ ++ fd->socket = NULL; ++ fd->net = file->device->net; ++ ++ ip_to_x (&fd->net->my_addr, ipx); ++ ++ for (j = 0; j < 2;++j) ++ { ++ for (i = 8; i >= 0; --i) ++ { ++ ++ grub_strcpy (fd->path, file->device->net->dhcp_file); ++ p = grub_strrchr (fd->path, '/'); ++ if (p) ++ { ++ p++; ++ } ++ else ++ { ++ p = fd->path; ++ } ++ if (!j) { ++#ifdef __x86_64__ ++ *(p++)='x'; ++ *(p++)='8'; ++ *(p++)='6'; ++ *(p++)='_'; ++ *(p++)='6'; ++ *(p++)='4'; ++#else ++ *(p++)='i'; ++ *(p++)='a'; ++ *(p++)='3'; ++ *(p++)='2'; ++#endif ++ *(p++)='/'; ++ } ++ grub_strcpy (p, ipx); ++ p += i; ++ *(p++) = '/'; ++ grub_strcpy (p, name); ++ ++ grub_dprintf ("tftp","tftp_open_called for %s\n", fd->path); ++ ++ ret = tftp (fd); ++ if (ret >= 0) { ++ file->size = fd->length; ++ file->offset = 0; ++ ++ return GRUB_ERR_NONE; ++ } ++ } ++ } ++ ++ fail: ++ ++ if (fd && fd->buf) ++ grub_free (fd->buf); ++ ++ if (fd) ++ grub_free (fd); ++ grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); ++ ++ grub_dl_unref (my_mod); ++ ++ return grub_errno; ++} ++ ++static grub_ssize_t ++grub_tftp_read (grub_file_t file, char *buf, grub_size_t len) ++{ ++ tftp_file_data_t *fd = (tftp_file_data_t *) file->data; ++ ++ if (len > file->size - file->offset) ++ len = file->size - file->offset; ++ ++ if (len <= 0) ++ return 0; ++ ++ grub_memcpy (buf, &fd->buf[file->offset], len); ++ ++ return len; ++} ++ ++ ++static grub_err_t ++grub_tftp_close (grub_file_t file) ++{ ++ tftp_file_data_t *fd = (tftp_file_data_t *) file->data; ++ ++ if (fd && fd->buf) ++ grub_free (fd->buf); ++ if (fd) ++ grub_free (fd); ++ ++ grub_dl_unref (my_mod); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_tftp_dir (grub_device_t device, const char *path, ++ int (*hook) (const char *filename, ++ const struct grub_dirhook_info *info)) ++{ ++ (void) device; ++ (void) path; ++ (void) hook; ++ ++ grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); ++ return grub_errno; ++} ++ ++static struct grub_fs grub_fs_tftp = { ++ .name = "tftp", ++ .dir = grub_tftp_dir, ++ .open = grub_tftp_open, ++ .read = grub_tftp_read, ++ .close = grub_tftp_close, ++ .next = 0 ++}; ++ ++GRUB_MOD_INIT(tftp) ++{ ++ grub_fs_register (&grub_fs_tftp); ++ my_mod = mod; ++} ++ ++GRUB_MOD_FINI(tftp) ++{ ++ grub_fs_unregister (&grub_fs_tftp); ++} +diff --git a/grub-core/kern/device.c b/grub-core/kern/device.c +index 3db14f5..13e4dee 100644 +--- a/grub-core/kern/device.c ++++ b/grub-core/kern/device.c +@@ -30,6 +30,7 @@ grub_device_t + grub_device_open (const char *name) + { + grub_disk_t disk = 0; ++ grub_net_t net = 0; + grub_device_t dev = 0; + + if (! name) +@@ -48,7 +49,12 @@ grub_device_open (const char *name) + + /* Try to open a disk. */ + disk = grub_disk_open (name); +- if (! disk) ++ if (! disk) { ++ grub_errno = GRUB_ERR_NONE; ++ net = grub_net_open(name); ++ } ++ ++ if (!disk && !net) + goto fail; + + dev->disk = disk; +@@ -57,6 +63,8 @@ grub_device_open (const char *name) + return dev; + + fail: ++ if (net) ++ grub_net_close (net); + if (disk) + grub_disk_close (disk); + +@@ -68,6 +76,8 @@ grub_device_open (const char *name) + grub_err_t + grub_device_close (grub_device_t device) + { ++ if (device->net) ++ grub_net_close (device->net); + if (device->disk) + grub_disk_close (device->disk); + +diff --git a/grub-core/kern/net.c b/grub-core/kern/net.c +new file mode 100644 +index 0000000..49eecb5 +--- /dev/null ++++ b/grub-core/kern/net.c +@@ -0,0 +1,1409 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2002,2003,2004,2006,2007,2008 Free Software Foundation, Inc. ++ * ++ * GRUB 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 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB 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 GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ /*PORTS*/ ++#define DHCPS_PORT 67 ++#define DHCPC_PORT 68 ++static int net_blocked = 0; ++ ++static grub_net_dev_t grub_net_dev_list; ++static grub_net_t grub_net_list; ++ ++static void net_dispatch (grub_net_t); ++ ++grub_udp_socket_t *udp_listeners = NULL; ++ ++#undef grub_dprintf ++#define grub_dprintf(a,b...) grub_printf(b) ++ ++/*BYTE SEX*/ ++static inline grub_uint16_t ++net_maybeswap16 (grub_uint16_t i) ++{ ++ grub_uint16_t ret; ++ ++ *((grub_uint8_t *) & ret) = i >> 8; ++ *(((grub_uint8_t *) & ret) + 1) = i & 0xff; ++ ++ return ret; ++} ++ ++grub_uint16_t ++grub_net_htons (grub_uint16_t i) ++{ ++ return net_maybeswap16 (i); ++} ++ ++grub_uint16_t ++grub_net_ntohs (grub_uint16_t i) ++{ ++ return net_maybeswap16 (i); ++} ++ ++static inline grub_uint32_t ++net_maybeswap32 (grub_uint32_t i) ++{ ++ grub_uint32_t ret; ++ ++ *((grub_uint8_t *) & ret) = i >> 24; ++ *(((grub_uint8_t *) & ret) + 1) = (i >> 16) & 0xff; ++ *(((grub_uint8_t *) & ret) + 2) = (i >> 8) & 0xff; ++ *(((grub_uint8_t *) & ret) + 3) = i & 0xff; ++ ++ return ret; ++} ++ ++grub_uint32_t ++grub_net_htonl (grub_uint32_t i) ++{ ++ return net_maybeswap32 (i); ++} ++ ++grub_uint32_t ++grub_net_ntohl (grub_uint32_t i) ++{ ++ return net_maybeswap32 (i); ++} ++ ++typedef struct __attribute__ ((packed)) ++{ ++ grub_ether_addr dst; ++ grub_ether_addr src; ++ grub_uint16_t type; ++ grub_uint8_t data[0]; ++} ether_hdr; ++ ++#define ETH_P_IP 0x0800 ++#define ETH_P_ARP 0x0806 ++ ++static grub_ether_addr ether_broadcast = ++ { {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} }; ++ ++ ++ ++ ++static grub_err_t ++ether_send (grub_net_t net, grub_uint16_t type, grub_uint8_t * buf, ++ int len, grub_ether_addr * dst) ++{ ++ grub_uint8_t frame[GRUB_ETH_FRAME_LEN]; ++ ether_hdr *h; ++ ++ h = (ether_hdr *) frame; ++ ++ if (len > GRUB_ETH_DATA_LEN) ++ len = GRUB_ETH_DATA_LEN; ++ ++ grub_memcpy (&h->src, &net->my_eth_addr, GRUB_ETH_ALEN); ++ grub_memcpy (&h->dst, dst, GRUB_ETH_ALEN); ++ ++ h->type = grub_net_htons (type); ++ grub_memcpy (h->data, buf, len); ++ ++ return net->dev->send (net, frame, len + GRUB_ETH_HLEN); ++} ++ ++static void ip_new_frame (grub_net_t, grub_uint8_t *, int, grub_ether_addr *); ++static void arp_new_frame (grub_net_t, grub_uint8_t *, int, ++ grub_ether_addr *); ++ ++static void ++ether_new_frame (grub_net_t net, grub_uint8_t * frame, int len) ++{ ++ ether_hdr *h; ++ int type; ++ ++ if (len < GRUB_ETH_HLEN) ++ return; ++ if (len > GRUB_ETH_FRAME_LEN) ++ return; ++ ++ h = (ether_hdr *) frame; ++ ++ if (grub_memcmp (&h->dst, &net->my_eth_addr, GRUB_ETH_ALEN) && ++ grub_memcmp (&h->dst, ðer_broadcast, GRUB_ETH_ALEN)) ++ return; ++ ++ type = grub_net_ntohs (h->type); ++ ++ frame += GRUB_ETH_HLEN; ++ len -= GRUB_ETH_HLEN; ++ ++ ++ switch (type) ++ { ++ case ETH_P_IP: ++ ip_new_frame (net, frame, len, &h->src); ++ break; ++ case ETH_P_ARP: ++ arp_new_frame (net, frame, len, &h->src); ++ break; ++ } ++} ++ ++ /* ARP */ ++#define ARPHRD_ETHER 1 ++#define ARPOP_REQUEST 1 ++#define ARPOP_REPLY 2 ++#define ARP_TABLE_SIZE 16 ++typedef struct __attribute__ ((packed)) ++{ ++ grub_uint16_t hwaddr_type; ++ grub_uint16_t paddr_type; ++ grub_uint8_t hwaddr_len; ++ grub_uint8_t paddr_len; ++ grub_uint16_t op; ++ grub_uint8_t data[0]; ++} arp_hdr; ++ ++typedef struct __attribute__ ((packed)) ++{ ++ arp_hdr h; ++ grub_ether_addr hw_sender; ++ grub_ipv4_addr p_sender; ++ grub_ether_addr hw_target; ++ grub_ipv4_addr p_target; ++} arp_hdr_eth_ipv4; ++ ++typedef enum ++{ ++ ARP_STATE_EMPTY = 0, ++ ARP_STATE_UNRESOLVED, ++ ARP_STATE_RESOLVED ++} arp_state; ++ ++ ++typedef struct ++{ ++ arp_state state; ++ grub_ipv4_addr ip; ++ grub_ether_addr mac; ++} arp_entry; ++ ++ ++static arp_entry arp_table[ARP_TABLE_SIZE]; ++static int arp_next_entry; ++ ++ ++static void ++arp_add_hw (grub_ipv4_addr * ip, grub_ether_addr * mac) ++{ ++ int i; ++ ++ for (i = 0; i < ARP_TABLE_SIZE; ++i) ++ { ++ if (arp_table[i].state == ARP_STATE_EMPTY) ++ return; ++ if (!grub_memcmp (&arp_table[i].ip, ip, GRUB_IPV4_ALEN)) ++ { ++ grub_memcpy (&arp_table[i].mac, mac, GRUB_ETH_ALEN); ++ arp_table[i].state = ARP_STATE_RESOLVED; ++ return; ++ } ++ } ++} ++ ++static int ++arp_add_ip (grub_ipv4_addr * ip) ++{ ++ int i; ++ for (i = 0; i < ARP_TABLE_SIZE; ++i) ++ { ++ if (arp_table[i].state == ARP_STATE_EMPTY) ++ break; ++ if (!grub_memcmp (&arp_table[i].ip, ip, GRUB_IPV4_ALEN)) ++ return i; ++ } ++ ++ i = arp_next_entry; ++ arp_table[i].state = ARP_STATE_UNRESOLVED; ++ grub_memcpy (&arp_table[i].ip, ip, GRUB_IPV4_ALEN); ++ ++ arp_next_entry++; ++ if (arp_next_entry == ARP_TABLE_SIZE) ++ arp_next_entry = 0; ++ ++ return i; ++} ++ ++static void ++arp_send_reply (grub_net_t net, grub_ipv4_addr * i, grub_ether_addr * e) ++{ ++ ++ arp_hdr_eth_ipv4 ih; ++ ++ ih.h.hwaddr_type = grub_net_htons (ARPHRD_ETHER); ++ ih.h.paddr_type = grub_net_htons (ETH_P_IP); ++ ih.h.hwaddr_len = GRUB_ETH_ALEN; ++ ih.h.paddr_len = GRUB_IPV4_ALEN; ++ ih.h.op = grub_net_htons (ARPOP_REPLY); ++ ++ grub_memcpy (&ih.hw_sender, &net->my_eth_addr, GRUB_ETH_ALEN); ++ grub_memcpy (&ih.p_sender, &net->my_addr, GRUB_IPV4_ALEN); ++ grub_memcpy (&ih.hw_target, e, GRUB_ETH_ALEN); ++ grub_memcpy (&ih.p_target, i, GRUB_IPV4_ALEN); ++ ++ ether_send (net, ETH_P_ARP, (grub_uint8_t *) & ih, sizeof (ih), ++ ðer_broadcast); ++ ++} ++ ++static void ++arp_send_request (grub_net_t net, grub_ipv4_addr * i) ++{ ++ arp_hdr_eth_ipv4 ih; ++ ++ arp_add_ip (i); ++ ++ ih.h.hwaddr_type = grub_net_htons (ARPHRD_ETHER); ++ ih.h.paddr_type = grub_net_htons (ETH_P_IP); ++ ih.h.hwaddr_len = GRUB_ETH_ALEN; ++ ih.h.paddr_len = GRUB_IPV4_ALEN; ++ ih.h.op = grub_net_htons (ARPOP_REQUEST); ++ ++ grub_memcpy (&ih.hw_sender, &net->my_eth_addr, GRUB_ETH_ALEN); ++ grub_memcpy (&ih.p_sender, &net->my_addr, GRUB_IPV4_ALEN); ++ grub_memcpy (&ih.hw_target, ðer_broadcast, GRUB_ETH_ALEN); ++ grub_memcpy (&ih.p_target, i, GRUB_IPV4_ALEN); ++ ++ ether_send (net, ETH_P_ARP, (grub_uint8_t *) & ih, sizeof (ih), ++ ðer_broadcast); ++} ++ ++#if 0 /*-Wunused*/ ++static void ++arp_flush (void) ++{ ++ int i; ++ for (i = 0; i < ARP_TABLE_SIZE; ++i) ++ { ++ arp_table[i].state = ARP_STATE_EMPTY; ++ } ++} ++#endif ++ ++static void ++arp_new_frame (grub_net_t net, grub_uint8_t * arp, int len, ++ grub_ether_addr * src) ++{ ++ arp_hdr *h; ++ arp_hdr_eth_ipv4 *ih; ++ ++ (void) src; ++ ++ if (len < (int) sizeof (arp_hdr)) ++ return; ++ ++ h = (arp_hdr *) arp; ++ ++ if (grub_net_ntohs (h->hwaddr_type) != ARPHRD_ETHER) ++ return; ++ if (grub_net_ntohs (h->paddr_type) != ETH_P_IP) ++ return; ++ ++ if (h->hwaddr_len != GRUB_ETH_ALEN) ++ return; ++ if (h->paddr_len != GRUB_IPV4_ALEN) ++ return; ++ ++ ih = (arp_hdr_eth_ipv4 *) arp; ++ ++ arp_add_hw (&ih->p_sender, &ih->hw_sender); ++ ++ switch (grub_net_ntohs (h->op)) ++ { ++ case ARPOP_REQUEST: ++ if (grub_memcmp (&ih->p_target, &net->my_addr, GRUB_IPV4_ALEN)) ++ return; ++ arp_send_reply (net, &ih->p_sender, &ih->hw_sender); ++ break; ++ case ARPOP_REPLY: ++ arp_add_hw (&ih->p_target, &ih->hw_target); ++ break; ++ } ++} ++ ++ ++static grub_ether_addr * ++arp_get_mac (grub_net_t net, grub_ipv4_addr * ip) ++{ ++ int tries = 5; ++ int i; ++ grub_uint64_t t; ++ ++ i = arp_add_ip (ip); ++ ++ do ++ { ++ if (arp_table[i].state == ARP_STATE_RESOLVED) ++ { ++ return &arp_table[i].mac; ++ } ++ ++ t = grub_get_time_ms () + 1000; ++ ++ arp_send_request (net, ip); ++ ++ while ((arp_table[i].state == ARP_STATE_UNRESOLVED) && ++ (grub_get_time_ms () < t)) ++ { ++ net_dispatch (net); ++ } ++ } ++ while (tries--); ++ ++ return NULL; ++} ++ ++ ++ ++ ++ /* IP */ ++#define IPV4_ALEN 4 ++#define IP_PROTO_IP 0x0 ++#define IP_PROTO_ICMP 0x1 ++#define IP_PROTO_TCP 0x6 ++#define IP_PROTO_UDP 0x11 ++typedef struct __attribute__ ((packed)) ++{ ++ grub_uint8_t ihl:4, version:4; ++ grub_uint8_t tos; ++ grub_uint16_t total_len; ++ grub_uint16_t id; ++ grub_uint16_t frag_off; ++ grub_uint8_t ttl; ++ grub_uint8_t protocol; ++ grub_uint16_t header_sum; ++ grub_ipv4_addr src; ++ grub_ipv4_addr dst; ++ grub_uint8_t options[0]; ++} ipv4_header; ++ ++ ++#define IP_VERSION 4 ++#define IP_DEFAULT_TTL 64 ++#define IP_DEFAULT_TOS 0 ++ ++ ++static grub_ipv4_addr ip_broadcast = { {0xff, 0xff, 0xff, 0xff} }; ++ ++static int ++ip_is_broadcast (grub_ipv4_addr * a) ++{ ++ return !grub_memcmp (a, &ip_broadcast, GRUB_IPV4_ALEN); ++} ++ ++ ++static int ++ip_is_local (grub_net_t net, grub_ipv4_addr * a) ++{ ++ int i; ++ ++ for (i = 0; i < 4; ++i) ++ { ++ if ((a->s_addr[i] & net->my_mask.s_addr[i]) != ++ (net->my_addr.s_addr[i] & net->my_mask.s_addr[i])) ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static grub_uint16_t ++ip_checksum (grub_uint8_t * b, int len) ++{ ++ grub_uint32_t ret = 0; ++ ++ while (len > 1) ++ { ++ ret += *(grub_uint16_t *) b; ++ b += 2; ++ if (ret & 0x80000000) ++ ret = (ret & 0xFFFF) + (ret >> 16); ++ len -= 2; ++ } ++ ++ if (len) ++ ret += (grub_uint16_t) * b; ++ ++ while (ret >> 16) ++ ret = (ret & 0xFFFF) + (ret >> 16); ++ ++ ++ return ~ret; ++} ++static void udp_new_frame (grub_net_t, grub_uint8_t *, int, grub_ipv4_addr *); ++static void icmp_new_frame (grub_net_t, grub_uint8_t *, int, ++ grub_ipv4_addr *); ++ ++ ++ ++static void ++ip_new_frame (grub_net_t net, grub_uint8_t * ip, int len, ++ grub_ether_addr * src) ++{ ++ ipv4_header *h = (ipv4_header *) ip; ++ ++ (void) src; ++ ++ int plen; ++ grub_uint8_t *payload; ++ ++ if (len < (int) sizeof (ipv4_header)) ++ return; ++ ++ ++ if (h->version != IP_VERSION) ++ return; ++ ++ if (len < 4 * h->ihl) ++ return; ++ ++ ++ if (grub_memcmp (&h->dst, &net->my_addr, GRUB_IPV4_ALEN) ++ && !ip_is_broadcast (&h->dst)) ++ return; ++ ++ if (ip_checksum (ip, 4 * h->ihl)) ++ return; ++ ++ /* FIXME: we don't do fragments */ ++ if (grub_net_ntohs (h->frag_off) & 0x3fff) ++ return; ++ ++ plen = grub_net_ntohs (h->total_len); ++ if (plen > len) ++ return; ++ ++ payload = ip; ++ ++ plen -= 4 * h->ihl; ++ payload += 4 * h->ihl; ++ ++ switch (h->protocol) ++ { ++ case IP_PROTO_ICMP: ++ icmp_new_frame (net, payload, plen, &h->src); ++ break; ++ case IP_PROTO_UDP: ++ udp_new_frame (net, payload, plen, &h->src); ++ break; ++ } ++ ++} ++ ++ ++ ++ ++static int ++ip_send (grub_net_t net, grub_uint8_t protocol, grub_uint8_t * data, ++ int dlen, grub_ipv4_addr * dst) ++{ ++ grub_uint8_t packet[GRUB_IP_PACKET_LEN]; ++ int len; ++ ++ ipv4_header *h = (ipv4_header *) packet; ++ grub_ether_addr *edst; ++ ++ if (ip_is_broadcast (dst)) ++ { ++ edst = ðer_broadcast; ++ } ++ else if (ip_is_local (net, dst)) ++ { ++ edst = arp_get_mac (net, dst); ++ } ++ else ++ { ++ edst = arp_get_mac (net, &net->my_router); ++ } ++ ++ if (!edst) ++ return -1; ++ ++ ++ if (dlen > GRUB_IP_DATA_LEN) ++ dlen = GRUB_IP_DATA_LEN; ++ ++ len = dlen + sizeof (ipv4_header); ++ ++ h->version = IP_VERSION; ++ h->ihl = sizeof (ipv4_header) / 4; ++ h->tos = IP_DEFAULT_TOS; ++ h->total_len = grub_net_htons (len); ++ h->id = 0; ++ h->frag_off = grub_net_htons (0x4000); ++ h->ttl = IP_DEFAULT_TTL; ++ h->protocol = protocol; ++ h->header_sum = 0; ++ grub_memcpy (&h->src, &net->my_addr, GRUB_IPV4_ALEN); ++ grub_memcpy (&h->dst, dst, GRUB_IPV4_ALEN); ++ ++ h->header_sum = ip_checksum (packet, sizeof (ipv4_header)); ++ ++ ++ grub_memcpy (packet + sizeof (ipv4_header), data, dlen); ++ ++ return ether_send (net, ETH_P_IP, packet, len, edst); ++} ++ ++/* ICMP */ ++ ++#define ICMP_TYPE_ECHO 8 ++#define ICMP_TYPE_ECHO_REPLY 0 ++ ++ ++typedef struct __attribute__ ((packed)) ++{ ++ grub_uint8_t type; ++ grub_uint8_t code; ++ grub_uint16_t checksum; ++ grub_uint16_t id; ++ grub_uint16_t seq; ++ grub_uint8_t data[0]; ++} icmp_echo; ++ ++ ++ ++static void ++icmp_new_frame (grub_net_t net, grub_uint8_t * icmp, int len, ++ grub_ipv4_addr * src) ++{ ++ icmp_echo *e, *r; ++ grub_uint8_t reply[GRUB_IP_DATA_LEN]; ++ ++ if (len < (int) sizeof (icmp_echo)) ++ return; ++ ++ e = (icmp_echo *) icmp; ++ ++ if (e->type != ICMP_TYPE_ECHO) ++ return; ++ ++ grub_memcpy (reply, icmp, len); ++ ++ r = (icmp_echo *) reply; ++ r->type = ICMP_TYPE_ECHO_REPLY; ++ ++ ip_send (net, IP_PROTO_ICMP, reply, len, src); ++} ++ ++ ++ ++/* UDP */ ++ ++ ++ ++typedef struct __attribute__ ((packed)) ++{ ++ grub_uint16_t src_port; ++ grub_uint16_t dst_port; ++ grub_uint16_t length; ++ grub_uint16_t checksum; ++ grub_uint8_t data[0]; ++} udp_hdr; ++ ++static void dhcp_new_frame (grub_net_t, grub_uint8_t *, ++ int, grub_ipv4_addr *, grub_uint16_t); ++ ++ ++static void ++udp_new_frame (grub_net_t net, grub_uint8_t * udp, int len, ++ grub_ipv4_addr * src) ++{ ++ grub_udp_socket_t *s; ++ udp_hdr *h; ++ grub_uint8_t *payload; ++ int plen; ++ int port; ++ ++ ++ if (len < (int) sizeof (udp_hdr)) ++ return; ++ ++ h = (udp_hdr *) udp; ++ ++ plen = len - sizeof (udp_hdr); ++ payload = udp + sizeof (udp_hdr); ++ ++ port = grub_net_ntohs (h->dst_port); ++ ++ ++ switch (port) ++ { ++ case DHCPC_PORT: ++ dhcp_new_frame (net, payload, plen, src, grub_net_ntohs (h->src_port)); ++ break; ++ default: ++ ++ for (s = udp_listeners; s; s = s->next) ++ { ++ if (port == s->port) ++ { ++ s->new_frame (s, net, payload, plen, src, ++ grub_net_ntohs (h->src_port)); ++ } ++ } ++ } ++ ++} ++ ++int ++grub_udp_send (grub_net_t net, grub_uint16_t sport, grub_uint8_t * data, ++ int dlen, grub_ipv4_addr * dst, grub_uint16_t dport) ++{ ++ grub_uint8_t packet[GRUB_UDP_PACKET_LEN]; ++ udp_hdr *h = (udp_hdr *) packet; ++ int len; ++ ++ len = dlen + sizeof (udp_hdr); ++ ++ h->src_port = grub_net_htons (sport); ++ h->dst_port = grub_net_htons (dport); ++ h->length = grub_net_htons (len); ++ h->checksum = 0; ++ ++ grub_memcpy (&h->data, data, dlen); ++ ++ return ip_send (net, IP_PROTO_UDP, packet, len, dst); ++ ++} ++ ++ ++/* DHCP */ ++ ++#define DHCP_MAX_MESSAGE_LEN ((GRUB_UDP_DATA_LEN)-256) ++#define DHCP_DEFAULT_MESSAGE_LEN 548 ++#define DHCP_MAGIC_COOKIE_LEN 4 ++ ++#define DHCP_OP_BOOTREQUEST 1 ++#define DHCP_OP_BOOTREPLY 2 ++ ++#define DHCP_FLAGS_BROADCAST 0x8000 ++ ++#define DHCP_MSG_TYPE_DISCOVER 1 ++#define DHCP_MSG_TYPE_OFFER 2 ++#define DHCP_MSG_TYPE_REQUEST 3 ++#define DHCP_MSG_TYPE_DECLINE 4 ++#define DHCP_MSG_TYPE_ACK 5 ++#define DHCP_MSG_TYPE_NAK 6 ++#define DHCP_MSG_TYPE_RELEASE 7 ++#define DHCP_MSG_TYPE_INFORM 8 ++ ++ ++#define DHCP_OPTION_SUBNET 1 ++#define DHCP_OPTION_ROUTER 3 ++#define DHCP_OPTION_BROADCAST 28 ++#define DHCP_OPTION_REQUESTED_IP_ADDR 50 ++#define DHCP_OPTION_TAG_DHCP 53 ++#define DHCP_OPTION_SERVER_ID 54 ++#define DHCP_OPTION_TAG_PARAMETER_REQUEST 55 ++#define DHCP_OPTION_DHCP_MAX_MESSAGE_LEN 57 ++#define DHCP_OPTION_TFTP_SERVER_NAME 66 ++#define DHCP_OPTION_BOOTFILE_NAME 67 ++#define DHCP_OPTION_END 255 ++ ++ ++typedef struct PACKED ++{ ++ grub_uint8_t op; ++ grub_uint8_t htype; ++ grub_uint8_t hlen; ++ grub_uint8_t hops; ++ grub_uint32_t xid; ++ grub_uint16_t secs; ++ grub_uint16_t flags; ++ grub_ipv4_addr ciaddr; ++ grub_ipv4_addr yiaddr; ++ grub_ipv4_addr siaddr; ++ grub_ipv4_addr giaddr; ++ grub_uint8_t chaddr[16]; ++ grub_uint8_t sname[64]; ++ grub_uint8_t file[128]; ++ grub_uint8_t options[0]; ++} dhcp_packet; ++ ++ ++ ++#define DHCP_TRIES 5 ++#define DHCP_WAIT_FOR_OFFERS 5000 ++#define DHCP_WAIT_FOR_ACK 5000 ++ ++ ++static grub_uint8_t dhcp_magic_cookie[DHCP_MAGIC_COOKIE_LEN] = ++ { 99, 130, 83, 99 }; ++ ++ ++static grub_uint32_t dhcp_xid = 0xdeadbeef; ++ ++ ++ ++ ++static grub_uint8_t * ++dhcp_find_option (dhcp_packet * p, int len, int option) ++{ ++ grub_uint8_t *optr = p->options; ++ int ol; ++ ++ optr += DHCP_MAGIC_COOKIE_LEN; ++ len -= sizeof (dhcp_packet) + DHCP_MAGIC_COOKIE_LEN; ++ ++ while (len > 0) ++ { ++ if (*optr == option) ++ return optr; ++ if (*optr == DHCP_OPTION_END) ++ return NULL; ++ ++ optr++; ++ ol = *(optr++); ++ len -= 2; ++ optr += ol; ++ len -= ol; ++ } ++ ++ return NULL; ++} ++ ++static grub_uint8_t * ++dhcp_copy_option (grub_uint8_t * out, dhcp_packet * p, int len, int option) ++{ ++ grub_uint8_t *in; ++ int n; ++ ++ in = dhcp_find_option (p, len, option); ++ if (!in) ++ return out; ++ ++ *(out++) = *(in++); ++ n = *(out++) = *(in++); ++ while (n--) ++ { ++ *(out++) = *(in++); ++ } ++ return out; ++} ++ ++static void ++dhcp_find_ip_option (dhcp_packet * p, int len, int option, grub_ipv4_addr * a) ++{ ++ grub_uint8_t *opt; ++ ++ opt = dhcp_find_option (p, len, option); ++ if (!opt) ++ return; ++ ++ opt++; ++ if (*opt != GRUB_IPV4_ALEN) ++ return; ++ ++ opt++; ++ ++ memcpy (a, opt, GRUB_IPV4_ALEN); ++} ++ ++static grub_uint8_t * ++dhcp_add_generic_options (grub_uint8_t * optr, int dhcp_type) ++{ ++ grub_uint8_t *lptr; ++ memcpy (optr, &dhcp_magic_cookie, DHCP_MAGIC_COOKIE_LEN); ++ optr += DHCP_MAGIC_COOKIE_LEN; ++ ++ *(optr++) = DHCP_OPTION_TAG_DHCP; ++ *(optr++) = 1; ++ *(optr++) = dhcp_type; ++ *(optr++) = DHCP_OPTION_TAG_PARAMETER_REQUEST; ++ lptr = optr++; ++ *(optr++) = DHCP_OPTION_SUBNET; ++ *(optr++) = DHCP_OPTION_ROUTER; ++ *(optr++) = DHCP_OPTION_BROADCAST; ++ *(optr++) = DHCP_OPTION_REQUESTED_IP_ADDR; ++ *(optr++) = DHCP_OPTION_SERVER_ID; ++ *(optr++) = DHCP_OPTION_TFTP_SERVER_NAME; ++ *(optr++) = DHCP_OPTION_BOOTFILE_NAME; ++ *lptr = (grub_uint8_t) (optr - lptr) - 1; ++ *(optr++) = DHCP_OPTION_DHCP_MAX_MESSAGE_LEN; ++ *(optr++) = 2; ++ *(grub_uint16_t *) optr = grub_net_htons (DHCP_MAX_MESSAGE_LEN); ++ optr += 2; ++ ++ return optr; ++} ++ ++ ++static void ++dhcp_acknak (grub_net_t net, dhcp_packet * an, int len) ++{ ++ grub_uint8_t *opt; ++ ++ opt = dhcp_find_option (an, len, DHCP_OPTION_TAG_DHCP); ++ if (!opt) ++ return; ++ ++ switch (*(opt + 2)) ++ { ++ case DHCP_MSG_TYPE_NAK: ++ net->dhcp_state = GRUB_DHCP_STATE_OFFERS; ++ return; ++ case DHCP_MSG_TYPE_ACK: ++ break; ++ default: ++ return; ++ } ++ ++/* May the lord be praised we finally have an ACK */ ++/* Set all the rubbish */ ++ ++ memcpy (net->dhcp_file, &an->file, sizeof (net->dhcp_file)); ++ memcpy (&net->tftp_addr, &an->siaddr, GRUB_IPV4_ALEN); ++ memcpy (&net->my_addr, &an->yiaddr, GRUB_IPV4_ALEN); ++ ++ dhcp_find_ip_option (an, len, DHCP_OPTION_SUBNET, &net->my_mask); ++ dhcp_find_ip_option (an, len, DHCP_OPTION_ROUTER, &net->my_router); ++ dhcp_find_ip_option (an, len, DHCP_OPTION_SERVER_ID, &net->dhcp_addr); ++ ++ net->dhcp_state = GRUB_DHCP_STATE_BOUND; ++ ++} ++ ++ ++ ++static void ++dhcp_offer (grub_net_t net, dhcp_packet * off, int len) ++{ ++ grub_uint8_t packet[DHCP_DEFAULT_MESSAGE_LEN]; ++ dhcp_packet *req = (dhcp_packet *) packet; ++ grub_uint8_t *optr; ++ ++ grub_memset (packet, 0, DHCP_DEFAULT_MESSAGE_LEN); ++ ++ req->op = DHCP_OP_BOOTREQUEST; ++ req->htype = ARPHRD_ETHER; ++ req->hlen = GRUB_ETH_ALEN; ++ req->xid = dhcp_xid; ++ ++ req->flags = grub_net_htons (DHCP_FLAGS_BROADCAST); ++ ++ grub_memcpy (req->chaddr, &net->my_eth_addr, GRUB_ETH_ALEN); ++ ++ optr = dhcp_add_generic_options (req->options, DHCP_MSG_TYPE_REQUEST); ++ ++ optr = dhcp_copy_option (optr, off, len, DHCP_OPTION_SERVER_ID); ++ ++ *(optr++) = DHCP_OPTION_REQUESTED_IP_ADDR; ++ *(optr++) = GRUB_IPV4_ALEN; ++ memcpy (optr, &off->yiaddr, GRUB_IPV4_ALEN); ++ optr += 4; ++ ++ *(optr++) = DHCP_OPTION_END; ++ ++ net->dhcp_state = GRUB_DHCP_STATE_ACKNAK; ++ ++ grub_udp_send (net, DHCPC_PORT, packet, len, &ip_broadcast, DHCPS_PORT); ++} ++ ++ ++ ++static int ++dhcp_discover (grub_net_t net) ++{ ++ grub_uint8_t packet[DHCP_DEFAULT_MESSAGE_LEN]; ++ grub_uint8_t *optr; ++ dhcp_packet *dis = (dhcp_packet *) packet; ++ ++ grub_memset (packet, 0, DHCP_DEFAULT_MESSAGE_LEN); ++ ++ ++ dis->op = DHCP_OP_BOOTREQUEST; ++ dis->htype = ARPHRD_ETHER; ++ dis->hlen = GRUB_ETH_ALEN; ++ dis->xid = dhcp_xid; ++ ++ dis->flags = grub_net_htons (DHCP_FLAGS_BROADCAST); ++ ++ memcpy (dis->chaddr, &net->my_eth_addr, GRUB_ETH_ALEN); ++ ++ optr = dhcp_add_generic_options (dis->options, DHCP_MSG_TYPE_DISCOVER); ++ *(optr++) = DHCP_OPTION_END; ++ ++ ++ return grub_udp_send (net, DHCPC_PORT, packet, sizeof (packet), ++ &ip_broadcast, DHCPS_PORT); ++} ++ ++ ++static void ++dhcp_new_frame (grub_net_t net, grub_uint8_t * data, ++ int len, grub_ipv4_addr * src, grub_uint16_t sport) ++{ ++ dhcp_packet *d; ++ ++ (void) src; ++ ++ if (sport != DHCPS_PORT) ++ return; ++ if (len > DHCP_MAX_MESSAGE_LEN) ++ return; ++ ++ if (len < (int) (sizeof (dhcp_packet) + 4)) ++ return; ++ ++ d = (dhcp_packet *) data; ++ ++ if (d->op != DHCP_OP_BOOTREPLY) ++ return; ++ if (d->htype != ARPHRD_ETHER) ++ return; ++ if (d->hlen != GRUB_ETH_ALEN) ++ return; ++ if (d->xid != dhcp_xid) ++ return; ++ if (grub_memcmp (d->chaddr, &net->my_eth_addr, GRUB_ETH_ALEN)) ++ return; ++ if (grub_memcmp (d->options, &dhcp_magic_cookie, DHCP_MAGIC_COOKIE_LEN)) ++ return; ++ ++ switch (net->dhcp_state) ++ { ++ case GRUB_DHCP_STATE_OFFERS: ++ ++ if (len > DHCP_MAX_MESSAGE_LEN) ++ return; ++ dhcp_offer (net, d, len); ++ return; ++ ++ case GRUB_DHCP_STATE_ACKNAK: ++ dhcp_acknak (net, d, len); ++ return; ++ } ++} ++ ++ ++ ++ ++/* slight wizzardry, fails go to STATE_OFFERS so it times out and */ ++/* decrements the try counter before returning to tu_dhcp_state discover */ ++/* this gives a back off and makes the logic simpler */ ++ ++ ++static int ++dhcp (grub_net_t net) ++{ ++ grub_uint64_t c; ++ int tries = DHCP_TRIES; ++ ++ while (net->dhcp_state != GRUB_DHCP_STATE_BOUND) ++ { ++ c = grub_get_time_ms (); ++ ++ switch (net->dhcp_state) ++ { ++ case GRUB_DHCP_STATE_DISCOVER: ++ net->dhcp_state = GRUB_DHCP_STATE_OFFERS; ++ dhcp_discover (net); ++ break; ++ case GRUB_DHCP_STATE_OFFERS: ++ while (((c + DHCP_WAIT_FOR_OFFERS) > grub_get_time_ms ()) ++ && (net->dhcp_state == GRUB_DHCP_STATE_OFFERS)) ++ net_dispatch (net); ++ if (net->dhcp_state == GRUB_DHCP_STATE_OFFERS) ++ { ++ if (!(tries--)) ++ { ++ net->dhcp_state = GRUB_DHCP_STATE_GAVE_UP; ++ return -1; ++ } ++ else ++ { ++ net->dhcp_state = GRUB_DHCP_STATE_DISCOVER; ++ } ++ } ++ break; ++ case GRUB_DHCP_STATE_REQUEST: ++ break; ++ case GRUB_DHCP_STATE_ACKNAK: ++ while (((c + DHCP_WAIT_FOR_ACK) > grub_get_time_ms ()) ++ && (net->dhcp_state == GRUB_DHCP_STATE_ACKNAK)) ++ net_dispatch (net); ++ ++ if (net->dhcp_state == GRUB_DHCP_STATE_ACKNAK) ++ { ++ net->dhcp_state = GRUB_DHCP_STATE_OFFERS; /* To decrement tries */ ++ } ++ break; ++ case GRUB_DHCP_STATE_BOUND: ++ return 0; ++ case GRUB_DHCP_STATE_GAVE_UP: ++ return -1; ++ default: ++ net->dhcp_state = GRUB_DHCP_STATE_DISCOVER; ++ } ++ } ++ return 0; ++} ++ ++ ++/* Network */ ++ ++static void ++net_dispatch (grub_net_t net) ++{ ++ grub_uint8_t frame[GRUB_ETH_FRAME_LEN]; ++ int len; ++ ++ len = sizeof (frame); ++ while (net->dev->recv (net, frame, &len) == GRUB_ERR_NONE) ++ { ++ if (!len) break; ++ ether_new_frame (net, frame, len); ++ len = sizeof (frame); ++ } ++ ++} ++ ++static int ++net_init (grub_net_t net) ++{ ++ if (net_blocked) ++ return 0; ++ ++ if ((!net->is_running) && (net->dev->start (net))) ++ return -1; ++ ++ if (net->is_up) ++ return 0; ++ ++ if (net->has_failed) ++ return -1; ++ ++ if (!net->is_up) ++ { ++ if (dhcp (net)) ++ { ++ net->has_failed++; ++ return -1; ++ } ++ net->is_up++; ++ } ++ ++ return 0; ++} ++ ++ ++void ++grub_net_dev_register (grub_net_dev_t dev) ++{ ++ dev->next = grub_net_dev_list; ++ grub_net_dev_list = dev; ++#if 1 ++ grub_net_syslog ("grub says: hello world"); ++#endif ++} ++ ++void ++grub_net_dev_unregister (grub_net_dev_t dev) ++{ ++ grub_net_dev_t *p, q; ++ ++ /* FIXME: XXX: we need to tear down active interfaces if their */ ++ /* drivers are being de-registered */ ++ ++ for (p = &grub_net_dev_list, q = *p; q; p = &(q->next), q = q->next) ++ if (q == dev) ++ { ++ *p = q->next; ++ break; ++ } ++} ++ ++static grub_fs_t find_tftp_fs(void) ++{ ++ grub_fs_t p; ++ ++ for (p = grub_fs_list; p; p = p->next) ++ { ++ if (!grub_strcmp(p->name,"tftp")) ++ return p; ++ } ++ ++ grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't find tftp filesystem to bind to net device"); ++ ++ return NULL; ++} ++ ++grub_net_t ++grub_net_open (const char *name) ++{ ++ grub_net_t net; ++ grub_net_dev_t dev; ++ ++ grub_dprintf ("net", "opening `%s'...\n", name); ++ ++ if (net_blocked) ++ return 0; ++ ++ if (name && !grub_strcmp (name, "net")) ++ { ++ name = NULL; ++ } ++ ++ /* See if we already have this device open */ ++ for (net = grub_net_list; net; net = net->next) ++ { ++ if (!name || !grub_strcmp (net->name, name)) ++ { ++ net_init (net); ++ return net->has_failed ? NULL:net; ++ } ++ } ++ for (dev = grub_net_dev_list; dev; dev = dev->next) ++ { ++ int ret; ++ grub_errno = GRUB_ERR_NONE; ++ ++ ++ net = (grub_net_t) grub_malloc (sizeof (*net)); ++ if (!net) ++ return 0; ++ ++ ++ net->name = NULL; ++ net->dhcp_state = GRUB_DHCP_STATE_DISCOVER; ++ net->is_running = 0; ++ net->is_up = 0; ++ net->has_failed = 0; ++ net->fs = find_tftp_fs(); ++ grub_memcpy (&net->my_addr, &ip_broadcast, GRUB_IPV4_ALEN); ++ ++ /*Try opening the name with this device sets net->name */ ++ ret=dev->open (name, net); ++ ++ grub_dprintf ("net", "opening `%s' gave %d.\n", name, ret); ++ ++ if (ret!=GRUB_ERR_NONE) { /*There was a problem*/ ++ ++ /*Fail - free*/ ++ if (net->name) ++ grub_free ((char *) net->name); ++ ++ grub_free(net); ++ net=NULL; ++ if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE) { ++ /*name made no sense - try next driver*/ ++ grub_errno=GRUB_ERR_NONE; ++ continue; ++ } else { ++ /*There was a problem - if we have a name, fail*/ ++ if (name) ++ goto fail; ++ /*we don't so try another device*/ ++ grub_errno=GRUB_ERR_NONE; ++ continue; ++ } ++ } ++ ++ /*The open worked add the opened device to the list*/ ++ net->dev = dev; ++ ++ net->next = grub_net_list; ++ grub_net_list = net; ++ ++ /*Attempt initalization*/ ++ net_init (net); ++ ++ /*Do we have a working network - yes hurrah all done*/ ++ if (net->is_up) ++ break; ++ ++ /*no - try others*/ ++ net=NULL; ++ } ++ ++ if (!dev) ++ { ++ grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such device"); ++ } ++ ++ ++fail: ++ ++ if (!net) ++ { ++ grub_dprintf ("net", "opening `%s' failed.\n", name); ++ } ++ ++ return net; ++} ++ ++static void ++net_open_default (void) ++{ ++ if (grub_net_list) ++ { ++ grub_net_open (grub_net_list->name); ++ } ++ else ++ { ++ grub_net_open (NULL); ++ } ++} ++ ++ ++void ++grub_net_close (grub_net_t net) ++{ ++ grub_dprintf ("net", "closing `%s'.\n", net->name); ++ ++ /* we don't actually close the network device as we're bound to want it later */ ++} ++ ++ ++void ++grub_net_dispatch (void) ++{ ++ grub_net_t net; ++ ++ for (net = grub_net_list; net; net = net->next) ++ { ++ if (net->is_running) ++ net_dispatch (net); ++ } ++} ++ ++void ++grub_net_shutdown (void) ++{ ++ grub_net_t net; ++ ++ while ((net = grub_net_list)) ++ { ++ grub_net_list = net->next; ++ ++ if (net->is_running) ++ { ++ net->dev->stop (net); ++ } ++ net->dev->close (net); ++ grub_free (net); ++ } ++ ++} ++ ++ ++ ++ ++/* syslog */ ++ ++#define SYSLOG_HDR_LEN 5 ++#define SYSLOG_PORT 514 ++ ++void ++grub_net_syslog (const char *msg) ++{ ++ grub_uint8_t buf[GRUB_UDP_DATA_LEN]; ++ grub_uint8_t *ptr; ++ int len; ++ int plen; ++ ++ grub_net_t net; ++ ++ if (net_blocked) ++ return; ++ ++ grub_error_push (); ++ ++ net_open_default (); ++ ++ len = sizeof (buf) - 1; ++ ptr = buf; ++ plen = 0; ++ ++ grub_memcpy (ptr, "<133>", SYSLOG_HDR_LEN); ++ ptr += SYSLOG_HDR_LEN; ++ len -= SYSLOG_HDR_LEN; ++ plen += SYSLOG_HDR_LEN; ++ ++ ++ while ((*msg) && len) ++ { ++ *(ptr++) = *(msg++); ++ len--; ++ plen++; ++ } ++ ++ *(ptr++) = 0; ++ plen++; ++ ++ for (net = grub_net_list; net; net = net->next) ++ { ++ if (net->is_running) ++ grub_udp_send (net, SYSLOG_PORT, buf, plen, &ip_broadcast, ++ SYSLOG_PORT); ++ } ++ grub_error_pop (); ++ ++} ++ ++ ++static grub_uint16_t udp_port = 32000; ++ ++grub_err_t ++grub_udp_bind (grub_udp_socket_t * socket) ++{ ++ grub_udp_socket_t *p; ++ ++ if (!socket->port) ++ socket->port = udp_port++; ++ socket->next = 0; ++ ++ for (p = udp_listeners; p; p = p->next) ++ { ++ if (p->port == socket->port) ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ ++ socket->next = udp_listeners; ++ udp_listeners = socket; ++ ++ return GRUB_ERR_NONE; ++} ++ ++grub_err_t ++grub_udp_unbind (grub_udp_socket_t * socket) ++{ ++ grub_udp_socket_t **p, *q; ++ ++ for (p = &udp_listeners, q = *p; q; p = &(q->next), q = q->next) ++ if (q == socket) ++ { ++ *p = q->next; ++ return GRUB_ERR_NONE; ++ } ++ ++ return GRUB_ERR_BAD_ARGUMENT; ++} ++ +diff --git a/include/grub/net.h b/include/grub/net.h +index c6d71d5..e056718 100644 +--- a/include/grub/net.h ++++ b/include/grub/net.h +@@ -23,50 +23,193 @@ + #include + #include + ++#define GRUB_ETH_FRAME_LEN 1514 ++#define GRUB_ETH_HLEN 14 ++#define GRUB_ETH_DATA_LEN (GRUB_ETH_FRAME_LEN - GRUB_ETH_HLEN) ++#define GRUB_IP_PACKET_LEN (GRUB_ETH_DATA_LEN) ++#define GRUB_IP_DATA_LEN ((GRUB_IP_PACKET_LEN) - 32) ++#define GRUB_UDP_PACKET_LEN (GRUB_IP_PACKET_LEN) ++#define GRUB_UDP_DATA_LEN ((GRUB_UDP_PACKET_LEN) - 8) ++ + struct grub_net; + ++#define GRUB_ETH_ALEN 6 ++ ++typedef struct ++{ ++ grub_uint8_t s_addr[GRUB_ETH_ALEN]; ++} grub_ether_addr; ++ ++#define GRUB_IPV4_ALEN 4 ++ ++typedef struct ++{ ++ grub_uint8_t s_addr[GRUB_IPV4_ALEN]; ++} grub_ipv4_addr; ++ ++ ++typedef enum ++{ ++ GRUB_DHCP_STATE_DISCOVER, ++ GRUB_DHCP_STATE_OFFERS, ++ GRUB_DHCP_STATE_REQUEST, ++ GRUB_DHCP_STATE_ACKNAK, ++ GRUB_DHCP_STATE_BOUND, ++ GRUB_DHCP_STATE_GAVE_UP ++} grub_dhcp_state; ++ ++/* The usual form for a network device is that it should ++ scan for compatible hardware upon module startup and ++ make an internal list of such devices. When the network ++ layer calls open it will either be with some user ++ specificed device name eg (rtl2) in which case the driver ++ should see if it recognises it (here the rtl driver should ++ look for the 3rd device it recognises) or NULL it which case ++ the driver is to look for the first device it recognises. ++ Open will then fill in the relevant parts of the struct grub_net ++ that it has been passed. ++ ++ start will then initalize and bring up the interface ++ setting net->is_running ++ ++ send and recv will then send and receive packets from the network ++ note that the recv function may not be called sufficiently often ++ to prevent an overflow in the inbound buffer of the card and the ++ driver should cope with this. A call to recv, should flush any ++ pending packets in the transmit queue before returning ++ ++ stop will prevent the network card from accessing host memory ++ until the next start making it safe for an operating system ++ boot or some other driver to interact with the card. ++ ++ close will free resources allocated in the open ++ (if net->name is freed, it must be set yo NULL) ++ ++ and the module unload function should tear down the list of ++ devices. NB the network stack MUST always call stop and close ++ before the the module unloads. ++ ++*/ ++ ++ + struct grub_net_dev + { +- /* The device name. */ +- const char *name; +- +- /* FIXME: Just a template. */ +- int (*probe) (struct grub_net *net, const void *addr); +- void (*reset) (struct grub_net *net); +- int (*poll) (struct grub_net *net); +- void (*transmit) (struct grub_net *net, const void *destip, +- unsigned srcsock, unsigned destsock, const void *packet); +- void (*disable) (struct grub_net *net); +- +- /* The next net device. */ +- struct grub_net_dev *next; ++ const char *name; /* Name of driver */ ++ ++ /* Open routine, name is either a specific ++ device name eg "rtl3" or is NULL to indicate ++ that the driver should attempt to configure ++ the first device in the system it supports ++ will return GRUB_ERR_NONE upon success and ++ GRUB_ERR_UNKNOWN_DEVICE if no such deivce ++ is found, the open routine shall not start ++ any DMA transactions. open shall fill in ++ net->name */ ++ grub_err_t (*open) (const char *name, struct grub_net * net); ++ ++ ++ ++ /* put the network card into a state ++ where it may receive and transmit. ++ start shall set net->is_running ++ on success and net->my_eth_addr ++ shall be set */ ++ grub_err_t (*start) (struct grub_net * net); ++ ++ /* put the network card into a state ++ where it shall not attempt to read ++ or write from host memory. stop shall ++ clear net->is_running */ ++ void (*stop) (struct grub_net * net); ++ ++ /* release the resources consumed by ++ open. If close frees net->name, it ++ MUST set net->name to NULL */ ++ void (*close) (struct grub_net * net); ++ ++ /* transmit one ethernet frame */ ++ grub_err_t (*send) (struct grub_net * net, const void *buf, int len); ++ ++ /* receive one ethernet frame, if no ++ frame is available recv shall return ++ GRUB_ERR_TIMEOUT and set *len to zero */ ++ grub_err_t (*recv) (struct grub_net * net, void *buf, int *len); ++ ++ /* The next net driver. */ ++ struct grub_net_dev *next; + }; + typedef struct grub_net_dev *grub_net_dev_t; + + struct grub_fs; + ++/* We keep a list of open network devices, because doing otherwise */ ++/* would require starting and stopping the network for each request */ + struct grub_net + { +- /* The net name. */ +- const char *name; ++ const char *name; /* Name of device, should be set with ++ grub_malloc-ed storage by the driver's ++ open routine */ + +- /* The underlying disk device. */ +- grub_net_dev_t dev; ++ grub_ether_addr my_eth_addr; ++ grub_ipv4_addr my_addr; ++ grub_ipv4_addr my_mask; ++ grub_ipv4_addr my_router; ++ grub_ipv4_addr dhcp_addr; ++ grub_ipv4_addr tftp_addr; + +- /* The binding filesystem. */ +- struct grub_fs *fs; ++ char dhcp_file[128]; + +- /* FIXME: More data would be required, such as an IP address, a mask, +- a gateway, etc. */ ++ int dhcp_state; + +- /* Device-specific data. */ +- void *data; ++ int is_running; /*Interface has been started - set ++ by dev->start */ ++ int is_up; /*Interface is up - dhcp has ++ succeded in getting an address */ ++ int has_failed; /*Interface has failed to come up - ++ used to prevent endless dhcp timeouts */ ++ ++ grub_net_dev_t dev; /* The underlying net device. */ ++ ++ struct grub_fs *fs; /* The binding filesystem. */ ++ ++ ++ void *data; /* Device-instance-specific data. */ ++ ++ struct grub_net *next; + }; + typedef struct grub_net *grub_net_t; + +-/* FIXME: How to abstract networks? More consideration is necessary. */ + +-/* Note: Networks are very different from disks, because networks must +- be initialized before used, and the status is persistent. */ ++struct grub_udp_socket ++{ ++ int port; ++ void (*new_frame) (struct grub_udp_socket * socket, grub_net_t net, ++ grub_uint8_t * msg, int len, grub_ipv4_addr * src, ++ grub_uint16_t src_port); ++ void *data; ++ struct grub_udp_socket *next; ++}; ++typedef struct grub_udp_socket grub_udp_socket_t; ++ ++void EXPORT_FUNC (grub_net_dev_register) (grub_net_dev_t dev); ++void EXPORT_FUNC (grub_net_dev_unregister) (grub_net_dev_t dev); ++grub_net_t EXPORT_FUNC (grub_net_open) (const char *name); ++void EXPORT_FUNC (grub_net_close) (grub_net_t net); ++void EXPORT_FUNC (grub_net_shutdown) (void); ++void EXPORT_FUNC (grub_net_dispatch) (void); ++void EXPORT_FUNC (grub_net_syslog) (const char *msg); ++ ++void EXPORT_FUNC (grub_net_dispatch) (void); ++int EXPORT_FUNC (grub_udp_send) (grub_net_t net, grub_uint16_t sport, ++ grub_uint8_t * data, int dlen, ++ grub_ipv4_addr * dst, grub_uint16_t dport); ++grub_err_t EXPORT_FUNC (grub_udp_bind) (grub_udp_socket_t * socket); ++grub_err_t EXPORT_FUNC (grub_udp_unbind) (grub_udp_socket_t * socket); ++ ++grub_uint32_t EXPORT_FUNC (grub_net_htonl) (grub_uint32_t); ++grub_uint32_t EXPORT_FUNC (grub_net_ntohl) (grub_uint32_t); ++grub_uint16_t EXPORT_FUNC (grub_net_htons) (grub_uint16_t); ++grub_uint16_t EXPORT_FUNC (grub_net_ntohs) (grub_uint16_t); ++ + + #endif /* ! GRUB_NET_HEADER */ diff --git a/master/series b/master/series index 823bd9a..8a47714 100644 --- a/master/series +++ b/master/series @@ -45,6 +45,16 @@ debian/efi_disk_cache.patch debian/dirlen.patch debian/hurd.patch debian/userland-part.patch +#jmm/efi_call_7 +jmm/efi_call_7-bis +#jmm/efi-load-drivers +jmm/efi-load-drivers-bis +#jmm/networking +#jmm/networking2 +jmm/networking-bis +#jmm/efi-sni +jmm/efi-sni-bis +no-prefix-bail endstop # # -- cgit v1.2.3