From 6e7a315eb67cb6c113cf37e1d66c4f11a51a2b3e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 18:29:51 +0200 Subject: Adding upstream version 2.06. Signed-off-by: Daniel Baumann --- grub-core/net/drivers/efi/efinet.c | 404 +++++++++++++++++++++++ grub-core/net/drivers/emu/emunet.c | 116 +++++++ grub-core/net/drivers/i386/pc/pxe.c | 419 ++++++++++++++++++++++++ grub-core/net/drivers/ieee1275/ofnet.c | 565 +++++++++++++++++++++++++++++++++ grub-core/net/drivers/uboot/ubootnet.c | 161 ++++++++++ 5 files changed, 1665 insertions(+) create mode 100644 grub-core/net/drivers/efi/efinet.c create mode 100644 grub-core/net/drivers/emu/emunet.c create mode 100644 grub-core/net/drivers/i386/pc/pxe.c create mode 100644 grub-core/net/drivers/ieee1275/ofnet.c create mode 100644 grub-core/net/drivers/uboot/ubootnet.c (limited to 'grub-core/net/drivers') diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c new file mode 100644 index 0000000..5388f95 --- /dev/null +++ b/grub-core/net/drivers/efi/efinet.c @@ -0,0 +1,404 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010,2011 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 + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* GUID. */ +static grub_efi_guid_t net_io_guid = GRUB_EFI_SIMPLE_NETWORK_GUID; +static grub_efi_guid_t pxe_io_guid = GRUB_EFI_PXE_GUID; + +static grub_err_t +send_card_buffer (struct grub_net_card *dev, + struct grub_net_buff *pack) +{ + grub_efi_status_t st; + grub_efi_simple_network_t *net = dev->efi_net; + grub_uint64_t limit_time = grub_get_time_ms () + 4000; + void *txbuf; + + if (dev->txbusy) + while (1) + { + txbuf = NULL; + st = efi_call_3 (net->get_status, net, 0, &txbuf); + if (st != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_IO, + N_("couldn't send network packet")); + /* + Some buggy firmware could return an arbitrary address instead of the + txbuf address we trasmitted, so just check that txbuf is non NULL + for success. This is ok because we open the SNP protocol in + exclusive mode so we know we're the only ones transmitting on this + box and since we only transmit one packet at a time we know our + transmit was successfull. + */ + if (txbuf) + { + dev->txbusy = 0; + break; + } + if (limit_time < grub_get_time_ms ()) + return grub_error (GRUB_ERR_TIMEOUT, + N_("couldn't send network packet")); + } + + dev->last_pkt_size = (pack->tail - pack->data); + if (dev->last_pkt_size > dev->mtu) + dev->last_pkt_size = dev->mtu; + + grub_memcpy (dev->txbuf, pack->data, dev->last_pkt_size); + + st = efi_call_7 (net->transmit, net, 0, dev->last_pkt_size, + dev->txbuf, NULL, NULL, NULL); + if (st != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_IO, N_("couldn't send network packet")); + + /* + The card may have sent out the packet immediately - set txbusy + to 0 in this case. + Cases were observed where checking txbuf at the next call + of send_card_buffer() is too late: 0 is returned in txbuf and + we run in the GRUB_ERR_TIMEOUT case above. + Perhaps a timeout in the FW has discarded the recycle buffer. + */ + txbuf = NULL; + st = efi_call_3 (net->get_status, net, 0, &txbuf); + dev->txbusy = !(st == GRUB_EFI_SUCCESS && txbuf); + + return GRUB_ERR_NONE; +} + +static struct grub_net_buff * +get_card_packet (struct grub_net_card *dev) +{ + grub_efi_simple_network_t *net = dev->efi_net; + grub_err_t err; + grub_efi_status_t st; + grub_efi_uintn_t bufsize = dev->rcvbufsize; + struct grub_net_buff *nb; + int i; + + for (i = 0; i < 2; i++) + { + if (!dev->rcvbuf) + dev->rcvbuf = grub_malloc (dev->rcvbufsize); + if (!dev->rcvbuf) + return NULL; + + st = efi_call_7 (net->receive, net, NULL, &bufsize, + dev->rcvbuf, NULL, NULL, NULL); + if (st != GRUB_EFI_BUFFER_TOO_SMALL) + break; + dev->rcvbufsize = 2 * ALIGN_UP (dev->rcvbufsize > bufsize + ? dev->rcvbufsize : bufsize, 64); + grub_free (dev->rcvbuf); + dev->rcvbuf = 0; + } + + if (st != GRUB_EFI_SUCCESS) + return NULL; + + nb = grub_netbuff_alloc (bufsize + 2); + if (!nb) + return NULL; + + /* Reserve 2 bytes so that 2 + 14/18 bytes of ethernet header is divisible + by 4. So that IP header is aligned on 4 bytes. */ + if (grub_netbuff_reserve (nb, 2)) + { + grub_netbuff_free (nb); + return NULL; + } + grub_memcpy (nb->data, dev->rcvbuf, bufsize); + err = grub_netbuff_put (nb, bufsize); + if (err) + { + grub_netbuff_free (nb); + return NULL; + } + + return nb; +} + +static grub_err_t +open_card (struct grub_net_card *dev) +{ + grub_efi_simple_network_t *net; + + /* Try to reopen SNP exlusively to close any active MNP protocol instance + that may compete for packet polling + */ + net = grub_efi_open_protocol (dev->efi_handle, &net_io_guid, + GRUB_EFI_OPEN_PROTOCOL_BY_EXCLUSIVE); + if (net) + { + if (net->mode->state == GRUB_EFI_NETWORK_STOPPED + && efi_call_1 (net->start, net) != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_NET_NO_CARD, "%s: net start failed", + dev->name); + + if (net->mode->state == GRUB_EFI_NETWORK_STOPPED) + return grub_error (GRUB_ERR_NET_NO_CARD, "%s: card stopped", + dev->name); + + if (net->mode->state == GRUB_EFI_NETWORK_STARTED + && efi_call_3 (net->initialize, net, 0, 0) != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_NET_NO_CARD, "%s: net initialize failed", + dev->name); + + /* Enable hardware receive filters if driver declares support for it. + We need unicast and broadcast and additionaly all nodes and + solicited multicast for IPv6. Solicited multicast is per-IPv6 + address and we currently do not have API to do it so simply + try to enable receive of all multicast packets or evertyhing in + the worst case (i386 PXE driver always enables promiscuous too). + + This does trust firmware to do what it claims to do. + */ + if (net->mode->receive_filter_mask) + { + grub_uint32_t filters = GRUB_EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | + GRUB_EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST | + GRUB_EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST; + + filters &= net->mode->receive_filter_mask; + if (!(filters & GRUB_EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST)) + filters |= (net->mode->receive_filter_mask & + GRUB_EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS); + + efi_call_6 (net->receive_filters, net, filters, 0, 0, 0, NULL); + } + + efi_call_4 (grub_efi_system_table->boot_services->close_protocol, + dev->efi_net, &net_io_guid, + grub_efi_image_handle, dev->efi_handle); + dev->efi_net = net; + } + + /* If it failed we just try to run as best as we can */ + return GRUB_ERR_NONE; +} + +static void +close_card (struct grub_net_card *dev) +{ + efi_call_1 (dev->efi_net->shutdown, dev->efi_net); + efi_call_1 (dev->efi_net->stop, dev->efi_net); + efi_call_4 (grub_efi_system_table->boot_services->close_protocol, + dev->efi_net, &net_io_guid, + grub_efi_image_handle, dev->efi_handle); +} + +static struct grub_net_card_driver efidriver = + { + .name = "efinet", + .open = open_card, + .close = close_card, + .send = send_card_buffer, + .recv = get_card_packet + }; + +grub_efi_handle_t +grub_efinet_get_device_handle (struct grub_net_card *card) +{ + if (!card || card->driver != &efidriver) + return 0; + return card->efi_handle; +} + +static void +grub_efinet_findcards (void) +{ + grub_efi_uintn_t num_handles; + grub_efi_handle_t *handles; + grub_efi_handle_t *handle; + int i = 0; + + /* Find handles which support the disk io interface. */ + handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &net_io_guid, + 0, &num_handles); + if (! handles) + return; + for (handle = handles; num_handles--; handle++) + { + grub_efi_simple_network_t *net; + struct grub_net_card *card; + grub_efi_device_path_t *dp, *parent = NULL, *child = NULL; + + /* EDK2 UEFI PXE driver creates IPv4 and IPv6 messaging devices as + children of main MAC messaging device. We only need one device with + bound SNP per physical card, otherwise they compete with each other + when polling for incoming packets. + */ + dp = grub_efi_get_device_path (*handle); + if (!dp) + continue; + for (; ! GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp); dp = GRUB_EFI_NEXT_DEVICE_PATH (dp)) + { + parent = child; + child = dp; + } + if (child + && GRUB_EFI_DEVICE_PATH_TYPE (child) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + && (GRUB_EFI_DEVICE_PATH_SUBTYPE (child) == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE + || GRUB_EFI_DEVICE_PATH_SUBTYPE (child) == GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE) + && parent + && GRUB_EFI_DEVICE_PATH_TYPE (parent) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (parent) == GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE) + continue; + + net = grub_efi_open_protocol (*handle, &net_io_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (! net) + /* This should not happen... Why? */ + continue; + + if (net->mode->state == GRUB_EFI_NETWORK_STOPPED + && efi_call_1 (net->start, net) != GRUB_EFI_SUCCESS) + continue; + + if (net->mode->state == GRUB_EFI_NETWORK_STOPPED) + continue; + + if (net->mode->state == GRUB_EFI_NETWORK_STARTED + && efi_call_3 (net->initialize, net, 0, 0) != GRUB_EFI_SUCCESS) + continue; + + card = grub_zalloc (sizeof (struct grub_net_card)); + if (!card) + { + grub_print_error (); + grub_free (handles); + return; + } + + card->mtu = net->mode->max_packet_size; + card->txbufsize = ALIGN_UP (card->mtu, 64) + 256; + card->txbuf = grub_zalloc (card->txbufsize); + if (!card->txbuf) + { + grub_print_error (); + grub_free (handles); + grub_free (card); + return; + } + card->txbusy = 0; + + card->rcvbufsize = ALIGN_UP (card->mtu, 64) + 256; + + card->name = grub_xasprintf ("efinet%d", i++); + card->driver = &efidriver; + card->flags = 0; + card->default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + grub_memcpy (card->default_address.mac, + net->mode->current_address, + sizeof (card->default_address.mac)); + card->efi_net = net; + card->efi_handle = *handle; + + grub_net_card_register (card); + } + grub_free (handles); +} + +static void +grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, + char **path) +{ + struct grub_net_card *card; + grub_efi_device_path_t *dp; + + dp = grub_efi_get_device_path (hnd); + if (! dp) + return; + + FOR_NET_CARDS (card) + { + grub_efi_device_path_t *cdp; + struct grub_efi_pxe *pxe; + struct grub_efi_pxe_mode *pxe_mode; + if (card->driver != &efidriver) + continue; + cdp = grub_efi_get_device_path (card->efi_handle); + if (! cdp) + continue; + if (grub_efi_compare_device_paths (dp, cdp) != 0) + { + grub_efi_device_path_t *ldp, *dup_dp, *dup_ldp; + int match; + + /* EDK2 UEFI PXE driver creates pseudo devices with type IPv4/IPv6 + as children of Ethernet card and binds PXE and Load File protocols + to it. Loaded Image Device Path protocol will point to these pseudo + devices. We skip them when enumerating cards, so here we need to + find matching MAC device. + */ + ldp = grub_efi_find_last_device_path (dp); + if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)) + continue; + dup_dp = grub_efi_duplicate_device_path (dp); + if (!dup_dp) + continue; + dup_ldp = grub_efi_find_last_device_path (dup_dp); + dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + dup_ldp->length = sizeof (*dup_ldp); + match = grub_efi_compare_device_paths (dup_dp, cdp) == 0; + grub_free (dup_dp); + if (!match) + continue; + } + pxe = grub_efi_open_protocol (hnd, &pxe_io_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (! pxe) + continue; + pxe_mode = pxe->mode; + grub_net_configure_by_dhcp_ack (card->name, card, 0, + (struct grub_net_bootp_packet *) + &pxe_mode->dhcp_ack, + sizeof (pxe_mode->dhcp_ack), + 1, device, path); + return; + } +} + +GRUB_MOD_INIT(efinet) +{ + grub_efinet_findcards (); + grub_efi_net_config = grub_efi_net_config_real; +} + +GRUB_MOD_FINI(efinet) +{ + struct grub_net_card *card, *next; + + FOR_NET_CARDS_SAFE (card, next) + if (card->driver == &efidriver) + grub_net_card_unregister (card); +} + diff --git a/grub-core/net/drivers/emu/emunet.c b/grub-core/net/drivers/emu/emunet.c new file mode 100644 index 0000000..b194920 --- /dev/null +++ b/grub-core/net/drivers/emu/emunet.c @@ -0,0 +1,116 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010,2011,2012,2013 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 + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +send_card_buffer (struct grub_net_card *dev __attribute__ ((unused)), + struct grub_net_buff *pack); + +static struct grub_net_buff * +get_card_packet (struct grub_net_card *dev __attribute__ ((unused))); + +static struct grub_net_card_driver emudriver = + { + .name = "emu", + .send = send_card_buffer, + .recv = get_card_packet + }; + +static struct grub_net_card emucard = + { + .name = "emu0", + .driver = &emudriver, + .mtu = 1500, + .default_address = { + .type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET, + {.mac = {0, 1, 2, 3, 4, 5}} + }, + .flags = 0 + }; + +static grub_err_t +send_card_buffer (struct grub_net_card *dev __attribute__ ((unused)), + struct grub_net_buff *pack) +{ + grub_ssize_t actual; + + actual = grub_emunet_send (pack->data, pack->tail - pack->data); + if (actual < 0) + return grub_error (GRUB_ERR_IO, N_("couldn't send network packet")); + + return GRUB_ERR_NONE; +} + +static struct grub_net_buff * +get_card_packet (struct grub_net_card *dev __attribute__ ((unused))) +{ + grub_ssize_t actual; + struct grub_net_buff *nb; + + nb = grub_netbuff_alloc (emucard.mtu + 36 + 2); + if (!nb) + return NULL; + + /* Reserve 2 bytes so that 2 + 14/18 bytes of ethernet header is divisible + by 4. So that IP header is aligned on 4 bytes. */ + grub_netbuff_reserve (nb, 2); + if (!nb) + { + grub_netbuff_free (nb); + return NULL; + } + + actual = grub_emunet_receive (nb->data, emucard.mtu + 36); + if (actual < 0) + { + grub_netbuff_free (nb); + return NULL; + } + grub_netbuff_put (nb, actual); + + return nb; +} + +static int registered = 0; + +GRUB_MOD_INIT(emunet) +{ + if (!grub_emunet_create (&emucard.mtu)) + { + grub_net_card_register (&emucard); + registered = 1; + } +} + +GRUB_MOD_FINI(emunet) +{ + if (registered) + { + grub_emunet_close (); + grub_net_card_unregister (&emucard); + registered = 0; + } +} diff --git a/grub-core/net/drivers/i386/pc/pxe.c b/grub-core/net/drivers/i386/pc/pxe.c new file mode 100644 index 0000000..3f4152d --- /dev/null +++ b/grub-core/net/drivers/i386/pc/pxe.c @@ -0,0 +1,419 @@ +/* pxe.c - Driver to provide access to the pxe filesystem */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009,2011 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 + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define SEGMENT(x) ((x) >> 4) +#define OFFSET(x) ((x) & 0xF) +#define SEGOFS(x) ((SEGMENT(x) << 16) + OFFSET(x)) +#define LINEAR(x) (void *) ((((x) >> 16) << 4) + ((x) & 0xFFFF)) + +struct grub_pxe_undi_open +{ + grub_uint16_t status; + grub_uint16_t open_flag; + grub_uint16_t pkt_filter; + grub_uint16_t mcast_count; + grub_uint8_t mcast[8][6]; +} GRUB_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; +} GRUB_PACKED; + + +struct grub_pxe_undi_isr +{ + grub_uint16_t status; + grub_uint16_t func_flag; + grub_uint16_t buffer_len; + grub_uint16_t frame_len; + grub_uint16_t frame_hdr_len; + grub_uint32_t buffer; + grub_uint8_t prot_type; + grub_uint8_t pkt_type; +} GRUB_PACKED; + +enum + { + GRUB_PXE_ISR_IN_START = 1, + GRUB_PXE_ISR_IN_PROCESS, + GRUB_PXE_ISR_IN_GET_NEXT + }; + +enum + { + GRUB_PXE_ISR_OUT_OURS = 0, + GRUB_PXE_ISR_OUT_NOT_OURS = 1 + }; + +enum + { + GRUB_PXE_ISR_OUT_DONE = 0, + GRUB_PXE_ISR_OUT_TRANSMIT = 2, + GRUB_PXE_ISR_OUT_RECEIVE = 3, + GRUB_PXE_ISR_OUT_BUSY = 4, + }; + +struct grub_pxe_undi_transmit +{ + grub_uint16_t status; + grub_uint8_t protocol; + grub_uint8_t xmitflag; + grub_uint32_t dest; + grub_uint32_t tbd; + grub_uint32_t reserved[2]; +} GRUB_PACKED; + +struct grub_pxe_undi_tbd +{ + grub_uint16_t len; + grub_uint32_t buf; + grub_uint16_t blk_count; + struct + { + grub_uint8_t ptr_type; + grub_uint8_t reserved; + grub_uint16_t len; + grub_uint32_t ptr; + } blocks[8]; +} GRUB_PACKED; + +struct grub_pxe_bangpxe *grub_pxe_pxenv; +static grub_uint32_t pxe_rm_entry = 0; + +static struct grub_pxe_bangpxe * +grub_pxe_scan (void) +{ + struct grub_bios_int_registers regs; + struct grub_pxenv *pxenv; + struct grub_pxe_bangpxe *bangpxe; + + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + + regs.ebx = 0; + regs.ecx = 0; + regs.eax = 0x5650; + regs.es = 0; + + grub_bios_interrupt (0x1a, ®s); + + if ((regs.eax & 0xffff) != 0x564e) + return NULL; + + pxenv = (struct grub_pxenv *) ((regs.es << 4) + (regs.ebx & 0xffff)); + if (grub_memcmp (pxenv->signature, GRUB_PXE_SIGNATURE, + sizeof (pxenv->signature)) + != 0) + return NULL; + + if (pxenv->version < 0x201) + return NULL; + + bangpxe = (void *) ((((pxenv->pxe_ptr & 0xffff0000) >> 16) << 4) + + (pxenv->pxe_ptr & 0xffff)); + + if (!bangpxe) + return NULL; + + if (grub_memcmp (bangpxe->signature, GRUB_PXE_BANGPXE_SIGNATURE, + sizeof (bangpxe->signature)) != 0) + return NULL; + + pxe_rm_entry = bangpxe->rm_entry; + + return bangpxe; +} + +static struct grub_net_buff * +grub_pxe_recv (struct grub_net_card *dev __attribute__ ((unused))) +{ + struct grub_pxe_undi_isr *isr; + static int in_progress = 0; + grub_uint8_t *ptr, *end; + struct grub_net_buff *buf; + + isr = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; + + if (!in_progress) + { + grub_memset (isr, 0, sizeof (*isr)); + isr->func_flag = GRUB_PXE_ISR_IN_START; + grub_pxe_call (GRUB_PXENV_UNDI_ISR, isr, pxe_rm_entry); + /* Normally according to the specification we should also check + that isr->func_flag != GRUB_PXE_ISR_OUT_OURS but unfortunately it + breaks on intel cards. + */ + if (isr->status) + { + in_progress = 0; + return NULL; + } + grub_memset (isr, 0, sizeof (*isr)); + isr->func_flag = GRUB_PXE_ISR_IN_PROCESS; + grub_pxe_call (GRUB_PXENV_UNDI_ISR, isr, pxe_rm_entry); + } + else + { + grub_memset (isr, 0, sizeof (*isr)); + isr->func_flag = GRUB_PXE_ISR_IN_GET_NEXT; + grub_pxe_call (GRUB_PXENV_UNDI_ISR, isr, pxe_rm_entry); + } + + while (isr->func_flag != GRUB_PXE_ISR_OUT_RECEIVE) + { + if (isr->status || isr->func_flag == GRUB_PXE_ISR_OUT_DONE) + { + in_progress = 0; + return NULL; + } + grub_memset (isr, 0, sizeof (*isr)); + isr->func_flag = GRUB_PXE_ISR_IN_GET_NEXT; + grub_pxe_call (GRUB_PXENV_UNDI_ISR, isr, pxe_rm_entry); + } + + buf = grub_netbuff_alloc (isr->frame_len + 2); + if (!buf) + return NULL; + /* Reserve 2 bytes so that 2 + 14/18 bytes of ethernet header is divisible + by 4. So that IP header is aligned on 4 bytes. */ + if (grub_netbuff_reserve (buf, 2)) + { + grub_netbuff_free (buf); + return NULL; + } + ptr = buf->data; + end = ptr + isr->frame_len; + grub_netbuff_put (buf, isr->frame_len); + grub_memcpy (ptr, LINEAR (isr->buffer), isr->buffer_len); + ptr += isr->buffer_len; + while (ptr < end) + { + grub_memset (isr, 0, sizeof (*isr)); + isr->func_flag = GRUB_PXE_ISR_IN_GET_NEXT; + grub_pxe_call (GRUB_PXENV_UNDI_ISR, isr, pxe_rm_entry); + if (isr->status || isr->func_flag != GRUB_PXE_ISR_OUT_RECEIVE) + { + in_progress = 1; + grub_netbuff_free (buf); + return NULL; + } + + grub_memcpy (ptr, LINEAR (isr->buffer), isr->buffer_len); + ptr += isr->buffer_len; + } + in_progress = 1; + + return buf; +} + +static grub_err_t +grub_pxe_send (struct grub_net_card *dev __attribute__ ((unused)), + struct grub_net_buff *pack) +{ + struct grub_pxe_undi_transmit *trans; + struct grub_pxe_undi_tbd *tbd; + char *buf; + + trans = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; + grub_memset (trans, 0, sizeof (*trans)); + tbd = (void *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + 128); + grub_memset (tbd, 0, sizeof (*tbd)); + buf = (void *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + 256); + grub_memcpy (buf, pack->data, pack->tail - pack->data); + + trans->tbd = SEGOFS ((grub_addr_t) tbd); + trans->protocol = 0; + tbd->len = pack->tail - pack->data; + tbd->buf = SEGOFS ((grub_addr_t) buf); + + grub_pxe_call (GRUB_PXENV_UNDI_TRANSMIT, trans, pxe_rm_entry); + if (trans->status) + return grub_error (GRUB_ERR_IO, N_("couldn't send network packet")); + return 0; +} + +static void +grub_pxe_close (struct grub_net_card *dev __attribute__ ((unused))) +{ + if (pxe_rm_entry) + grub_pxe_call (GRUB_PXENV_UNDI_CLOSE, + (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, + pxe_rm_entry); +} + +static grub_err_t +grub_pxe_open (struct grub_net_card *dev __attribute__ ((unused))) +{ + struct grub_pxe_undi_open *ou; + ou = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; + grub_memset (ou, 0, sizeof (*ou)); + ou->pkt_filter = 4; + grub_pxe_call (GRUB_PXENV_UNDI_OPEN, ou, pxe_rm_entry); + + if (ou->status) + return grub_error (GRUB_ERR_IO, "can't open UNDI"); + return GRUB_ERR_NONE; +} + +struct grub_net_card_driver grub_pxe_card_driver = +{ + .open = grub_pxe_open, + .close = grub_pxe_close, + .send = grub_pxe_send, + .recv = grub_pxe_recv +}; + +struct grub_net_card grub_pxe_card = +{ + .driver = &grub_pxe_card_driver, + .name = "pxe" +}; + +static grub_err_t +grub_pxe_shutdown (int flags) +{ + if (flags & GRUB_LOADER_FLAG_PXE_NOT_UNLOAD) + return GRUB_ERR_NONE; + if (!pxe_rm_entry) + return GRUB_ERR_NONE; + + grub_pxe_call (GRUB_PXENV_UNDI_CLOSE, + (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, + pxe_rm_entry); + grub_pxe_call (GRUB_PXENV_UNDI_SHUTDOWN, + (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, + pxe_rm_entry); + grub_pxe_call (GRUB_PXENV_UNLOAD_STACK, + (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, + pxe_rm_entry); + pxe_rm_entry = 0; + grub_net_card_unregister (&grub_pxe_card); + + return GRUB_ERR_NONE; +} + +/* Nothing we can do. */ +static grub_err_t +grub_pxe_restore (void) +{ + return GRUB_ERR_NONE; +} + +void * +grub_pxe_get_cached (grub_uint16_t type) +{ + struct grub_pxenv_get_cached_info ci; + ci.packet_type = type; + ci.buffer = 0; + ci.buffer_size = 0; + grub_pxe_call (GRUB_PXENV_GET_CACHED_INFO, &ci, pxe_rm_entry); + if (ci.status) + return 0; + + return LINEAR (ci.buffer); +} + +static void +grub_pc_net_config_real (char **device, char **path) +{ + struct grub_net_bootp_packet *bp; + + bp = grub_pxe_get_cached (GRUB_PXENV_PACKET_TYPE_DHCP_ACK); + + if (!bp) + return; + grub_net_configure_by_dhcp_ack ("pxe", &grub_pxe_card, 0, + bp, GRUB_PXE_BOOTP_SIZE, + 1, device, path); + +} + +static struct grub_preboot *fini_hnd; + +GRUB_MOD_INIT(pxe) +{ + struct grub_pxe_bangpxe *pxenv; + 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.mtu = ui->mtu; + + grub_pxe_card.default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + + grub_net_card_register (&grub_pxe_card); + grub_pc_net_config = grub_pc_net_config_real; + fini_hnd = grub_loader_register_preboot_hook (grub_pxe_shutdown, + grub_pxe_restore, + GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK); +} + +GRUB_MOD_FINI(pxe) +{ + grub_pc_net_config = 0; + grub_pxe_shutdown (0); + grub_loader_unregister_preboot_hook (fini_hnd); +} diff --git a/grub-core/net/drivers/ieee1275/ofnet.c b/grub-core/net/drivers/ieee1275/ofnet.c new file mode 100644 index 0000000..ac4e62a --- /dev/null +++ b/grub-core/net/drivers/ieee1275/ofnet.c @@ -0,0 +1,565 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010,2011 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 + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct grub_ofnetcard_data +{ + char *path; + char *suffix; + grub_ieee1275_ihandle_t handle; +}; + +static grub_err_t +card_open (struct grub_net_card *dev) +{ + int status; + struct grub_ofnetcard_data *data = dev->data; + + status = grub_ieee1275_open (data->path, &(data->handle)); + + if (status) + return grub_error (GRUB_ERR_IO, "Couldn't open network card."); + + return GRUB_ERR_NONE; +} + +static void +card_close (struct grub_net_card *dev) +{ + struct grub_ofnetcard_data *data = dev->data; + + if (data->handle) + grub_ieee1275_close (data->handle); +} + +static grub_err_t +send_card_buffer (struct grub_net_card *dev, struct grub_net_buff *pack) +{ + grub_ssize_t actual; + int status; + struct grub_ofnetcard_data *data = dev->data; + grub_size_t len; + + len = (pack->tail - pack->data); + if (len > dev->mtu) + len = dev->mtu; + + grub_memcpy (dev->txbuf, pack->data, len); + status = grub_ieee1275_write (data->handle, dev->txbuf, + len, &actual); + + if (status) + return grub_error (GRUB_ERR_IO, N_("couldn't send network packet")); + return GRUB_ERR_NONE; +} + +static struct grub_net_buff * +get_card_packet (struct grub_net_card *dev) +{ + grub_ssize_t actual; + int rc; + struct grub_ofnetcard_data *data = dev->data; + grub_uint64_t start_time; + struct grub_net_buff *nb; + + start_time = grub_get_time_ms (); + do + rc = grub_ieee1275_read (data->handle, dev->rcvbuf, dev->rcvbufsize, &actual); + while ((actual <= 0 || rc < 0) && (grub_get_time_ms () - start_time < 200)); + + if (actual <= 0) + return NULL; + + nb = grub_netbuff_alloc (actual + 2); + if (!nb) + return NULL; + /* Reserve 2 bytes so that 2 + 14/18 bytes of ethernet header is divisible + by 4. So that IP header is aligned on 4 bytes. */ + grub_netbuff_reserve (nb, 2); + + grub_memcpy (nb->data, dev->rcvbuf, actual); + + if (grub_netbuff_put (nb, actual)) + { + grub_netbuff_free (nb); + return NULL; + } + + return nb; +} + +static struct grub_net_card_driver ofdriver = + { + .name = "ofnet", + .open = card_open, + .close = card_close, + .send = send_card_buffer, + .recv = get_card_packet + }; + +static const struct +{ + const char *name; + int offset; +} + +bootp_response_properties[] = + { + { .name = "bootp-response", .offset = 0}, + { .name = "dhcp-response", .offset = 0}, + { .name = "bootpreply-packet", .offset = 0x2a}, + }; + +enum +{ + BOOTARGS_SERVER_ADDR, + BOOTARGS_FILENAME, + BOOTARGS_CLIENT_ADDR, + BOOTARGS_GATEWAY_ADDR, + BOOTARGS_BOOTP_RETRIES, + BOOTARGS_TFTP_RETRIES, + BOOTARGS_SUBNET_MASK, + BOOTARGS_BLOCKSIZE +}; + +static int +grub_ieee1275_parse_bootpath (const char *devpath, char *bootpath, + char **device, struct grub_net_card **card) +{ + char *args; + char *comma_char = 0; + char *equal_char = 0; + grub_size_t field_counter = 0; + grub_net_network_level_address_t client_addr = {0, {0}, 0}, gateway_addr = {0, {0}, 0}, subnet_mask = {0, {0}, 0}; + grub_net_link_level_address_t hw_addr = {0, {{0, 0, 0, 0, 0, 0}}}; + grub_net_interface_flags_t flags = 0; + struct grub_net_network_level_interface *inter = NULL; + grub_uint16_t vlantag = 0; + + hw_addr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + + args = bootpath + grub_strlen (devpath) + 1; + do + { + comma_char = grub_strchr (args, ','); + if (comma_char != 0) + *comma_char = 0; + + /* Check if it's an option (like speed=auto) and not a default parameter */ + equal_char = grub_strchr (args, '='); + if (equal_char != 0) + { + *equal_char = 0; + grub_env_set_net_property ((*card)->name, args, equal_char + 1, + grub_strlen(equal_char + 1)); + + if ((grub_strcmp (args, "vtag") == 0) && + (grub_strlen (equal_char + 1) == 8)) + vlantag = grub_strtoul (equal_char + 1 + 4, 0, 16); + + *equal_char = '='; + } + else + { + switch (field_counter++) + { + case BOOTARGS_SERVER_ADDR: + *device = grub_xasprintf ("tftp,%s", args); + if (!*device) + return grub_errno; + break; + + case BOOTARGS_CLIENT_ADDR: + grub_net_resolve_address (args, &client_addr); + break; + + case BOOTARGS_GATEWAY_ADDR: + grub_net_resolve_address (args, &gateway_addr); + break; + + case BOOTARGS_SUBNET_MASK: + grub_net_resolve_address (args, &subnet_mask); + break; + } + } + args = comma_char + 1; + if (comma_char != 0) + *comma_char = ','; + } while (comma_char != 0); + + if ((client_addr.ipv4 != 0) && (subnet_mask.ipv4 != 0)) + { + grub_ieee1275_phandle_t devhandle; + grub_ieee1275_finddevice (devpath, &devhandle); + grub_ieee1275_get_property (devhandle, "mac-address", + hw_addr.mac, sizeof(hw_addr.mac), 0); + inter = grub_net_add_addr ((*card)->name, *card, &client_addr, &hw_addr, + flags); + inter->vlantag = vlantag; + grub_net_add_ipv4_local (inter, + __builtin_ctz (~grub_le_to_cpu32 (subnet_mask.ipv4))); + + } + + if (gateway_addr.ipv4 != 0) + { + grub_net_network_level_netaddress_t target; + char *rname; + + target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + target.ipv4.base = 0; + target.ipv4.masksize = 0; + rname = grub_xasprintf ("%s:default", ((*card)->name)); + if (rname) + grub_net_add_route_gw (rname, target, gateway_addr, inter); + else + return grub_errno; + } + + return 0; +} + +static void +grub_ieee1275_net_config_real (const char *devpath, char **device, char **path, + char *bootpath) +{ + struct grub_net_card *card; + + /* FIXME: Check that it's the right card. */ + FOR_NET_CARDS (card) + { + char *bootp_response; + char *canon; + char c; + struct grub_ofnetcard_data *data; + + grub_ssize_t size = -1; + unsigned int i; + + if (card->driver != &ofdriver) + continue; + + data = card->data; + c = *data->suffix; + *data->suffix = '\0'; + canon = grub_ieee1275_canonicalise_devname (data->path); + *data->suffix = c; + if (grub_strcmp (devpath, canon) != 0) + { + grub_free (canon); + continue; + } + grub_free (canon); + + grub_ieee1275_parse_bootpath (devpath, bootpath, device, &card); + + for (i = 0; i < ARRAY_SIZE (bootp_response_properties); i++) + if (grub_ieee1275_get_property_length (grub_ieee1275_chosen, + bootp_response_properties[i].name, + &size) >= 0) + break; + + if (size < 0) + return; + + bootp_response = grub_malloc (size); + if (!bootp_response) + { + grub_print_error (); + return; + } + if (grub_ieee1275_get_property (grub_ieee1275_chosen, + bootp_response_properties[i].name, + bootp_response, size, 0) < 0) + return; + + grub_net_configure_by_dhcp_ack (card->name, card, 0, + (struct grub_net_bootp_packet *) + (bootp_response + + bootp_response_properties[i].offset), + size - bootp_response_properties[i].offset, + 1, device, path); + grub_free (bootp_response); + return; + } +} + +/* Allocate memory with alloc-mem */ +static void * +grub_ieee1275_alloc_mem (grub_size_t len) +{ + struct alloc_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t len; + grub_ieee1275_cell_t catch; + grub_ieee1275_cell_t result; + } + args; + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CANNOT_INTERPRET)) + { + grub_error (GRUB_ERR_UNKNOWN_COMMAND, N_("interpret is not supported")); + return NULL; + } + + INIT_IEEE1275_COMMON (&args.common, "interpret", 2, 2); + args.len = len; + args.method = (grub_ieee1275_cell_t) "alloc-mem"; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1 || args.catch) + { + grub_error (GRUB_ERR_INVALID_COMMAND, N_("alloc-mem failed")); + return NULL; + } + else + return (void *)args.result; +} + +/* Free memory allocated by alloc-mem */ +static grub_err_t +grub_ieee1275_free_mem (void *addr, grub_size_t len) +{ + struct free_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t len; + grub_ieee1275_cell_t addr; + grub_ieee1275_cell_t catch; + } + args; + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CANNOT_INTERPRET)) + { + grub_error (GRUB_ERR_UNKNOWN_COMMAND, N_("interpret is not supported")); + return grub_errno; + } + + INIT_IEEE1275_COMMON (&args.common, "interpret", 3, 1); + args.addr = (grub_ieee1275_cell_t)addr; + args.len = len; + args.method = (grub_ieee1275_cell_t) "free-mem"; + + if (IEEE1275_CALL_ENTRY_FN(&args) == -1 || args.catch) + { + grub_error (GRUB_ERR_INVALID_COMMAND, N_("free-mem failed")); + return grub_errno; + } + + return GRUB_ERR_NONE; +} + +static void * +ofnet_alloc_netbuf (grub_size_t len) +{ + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_VIRT_TO_REAL_BROKEN)) + return grub_ieee1275_alloc_mem (len); + else + return grub_zalloc (len); +} + +static void +ofnet_free_netbuf (void *addr, grub_size_t len) +{ + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_VIRT_TO_REAL_BROKEN)) + grub_ieee1275_free_mem (addr, len); + else + grub_free (addr); +} + +static int +search_net_devices (struct grub_ieee1275_devalias *alias) +{ + struct grub_ofnetcard_data *ofdata; + struct grub_net_card *card; + grub_ieee1275_phandle_t devhandle; + grub_net_link_level_address_t lla; + grub_ssize_t prop_size; + grub_uint64_t prop; + grub_uint8_t *pprop; + char *shortname; + char need_suffix = 1; + + if (grub_strcmp (alias->type, "network") != 0) + return 0; + + ofdata = grub_malloc (sizeof (struct grub_ofnetcard_data)); + if (!ofdata) + { + grub_print_error (); + return 1; + } + card = grub_zalloc (sizeof (struct grub_net_card)); + if (!card) + { + grub_free (ofdata); + grub_print_error (); + return 1; + } + +#define SUFFIX ":speed=auto,duplex=auto,1.1.1.1,dummy,1.1.1.1,1.1.1.1,5,5,1.1.1.1,512" + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_OFNET_SUFFIX)) + need_suffix = 0; + + /* sun4v vnet devices do not support setting duplex/speed */ + { + char *ptr; + + grub_ieee1275_finddevice (alias->path, &devhandle); + + grub_ieee1275_get_property_length (devhandle, "compatible", &prop_size); + if (prop_size > 0) + { + pprop = grub_malloc (prop_size); + if (!pprop) + { + grub_free (card); + grub_free (ofdata); + grub_print_error (); + return 1; + } + + if (!grub_ieee1275_get_property (devhandle, "compatible", + pprop, prop_size, NULL)) + { + for (ptr = (char *) pprop; ptr - (char *) pprop < prop_size; + ptr += grub_strlen (ptr) + 1) + { + if (!grub_strcmp(ptr, "SUNW,sun4v-network")) + need_suffix = 0; + } + } + + grub_free (pprop); + } + } + + if (need_suffix) + ofdata->path = grub_malloc (grub_strlen (alias->path) + sizeof (SUFFIX)); + else + ofdata->path = grub_malloc (grub_strlen (alias->path) + 1); + if (!ofdata->path) + { + grub_print_error (); + return 0; + } + ofdata->suffix = grub_stpcpy (ofdata->path, alias->path); + if (need_suffix) + grub_memcpy (ofdata->suffix, SUFFIX, sizeof (SUFFIX)); + else + *ofdata->suffix = '\0'; + + grub_ieee1275_finddevice (ofdata->path, &devhandle); + + { + grub_uint32_t t; + if (grub_ieee1275_get_integer_property (devhandle, + "max-frame-size", &t, + sizeof (t), 0)) + card->mtu = 1500; + else + card->mtu = t; + } + + pprop = (grub_uint8_t *) ∝ + if (grub_ieee1275_get_property (devhandle, "mac-address", + pprop, sizeof(prop), &prop_size) + && grub_ieee1275_get_property (devhandle, "local-mac-address", + pprop, sizeof(prop), &prop_size)) + { + grub_error (GRUB_ERR_IO, "Couldn't retrieve mac address."); + grub_print_error (); + return 0; + } + + if (prop_size == 8) + grub_memcpy (&lla.mac, pprop+2, 6); + else + grub_memcpy (&lla.mac, pprop, 6); + + lla.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + card->default_address = lla; + + card->txbufsize = ALIGN_UP (card->mtu, 64) + 256; + card->rcvbufsize = ALIGN_UP (card->mtu, 64) + 256; + + card->txbuf = ofnet_alloc_netbuf (card->txbufsize); + if (!card->txbuf) + goto fail_netbuf; + + card->rcvbuf = ofnet_alloc_netbuf (card->rcvbufsize); + if (!card->rcvbuf) + { + grub_error_push (); + ofnet_free_netbuf (card->txbuf, card->txbufsize); + grub_error_pop (); + goto fail_netbuf; + } + card->driver = NULL; + card->data = ofdata; + card->flags = 0; + shortname = grub_ieee1275_get_devname (alias->path); + card->name = grub_xasprintf ("ofnet_%s", shortname ? : alias->path); + card->idle_poll_delay_ms = 10; + grub_free (shortname); + + card->driver = &ofdriver; + grub_net_card_register (card); + return 0; + +fail_netbuf: + grub_free (ofdata->path); + grub_free (ofdata); + grub_free (card); + grub_print_error (); + return 0; +} + +static void +grub_ofnet_findcards (void) +{ + /* Look at all nodes for devices of the type network. */ + grub_ieee1275_devices_iterate (search_net_devices); +} + +GRUB_MOD_INIT(ofnet) +{ + grub_ofnet_findcards (); + grub_ieee1275_net_config = grub_ieee1275_net_config_real; +} + +GRUB_MOD_FINI(ofnet) +{ + struct grub_net_card *card, *next; + + FOR_NET_CARDS_SAFE (card, next) + if (card->driver && grub_strcmp (card->driver->name, "ofnet") == 0) + grub_net_card_unregister (card); + grub_ieee1275_net_config = 0; +} diff --git a/grub-core/net/drivers/uboot/ubootnet.c b/grub-core/net/drivers/uboot/ubootnet.c new file mode 100644 index 0000000..056052e --- /dev/null +++ b/grub-core/net/drivers/uboot/ubootnet.c @@ -0,0 +1,161 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +card_open (struct grub_net_card *dev) +{ + int status; + + status = grub_uboot_dev_open (dev->data); + if (status) + return grub_error (GRUB_ERR_IO, "Couldn't open network card."); + + return GRUB_ERR_NONE; +} + +static void +card_close (struct grub_net_card *dev) +{ + grub_uboot_dev_close (dev->data); +} + +static grub_err_t +send_card_buffer (struct grub_net_card *dev, struct grub_net_buff *pack) +{ + int status; + grub_size_t len; + + len = (pack->tail - pack->data); + if (len > dev->mtu) + len = dev->mtu; + + grub_memcpy (dev->txbuf, pack->data, len); + status = grub_uboot_dev_send (dev->data, dev->txbuf, len); + + if (status) + return grub_error (GRUB_ERR_IO, N_("couldn't send network packet")); + return GRUB_ERR_NONE; +} + +static struct grub_net_buff * +get_card_packet (struct grub_net_card *dev) +{ + int rc; + grub_uint64_t start_time; + struct grub_net_buff *nb; + int actual; + + nb = grub_netbuff_alloc (dev->mtu + 64 + 2); + if (!nb) + return NULL; + /* Reserve 2 bytes so that 2 + 14/18 bytes of ethernet header is divisible + by 4. So that IP header is aligned on 4 bytes. */ + grub_netbuff_reserve (nb, 2); + + start_time = grub_get_time_ms (); + do + { + rc = grub_uboot_dev_recv (dev->data, nb->data, dev->mtu + 64, &actual); + grub_dprintf ("net", "rc=%d, actual=%d, time=%lld\n", rc, actual, + grub_get_time_ms () - start_time); + } + while ((actual <= 0 || rc < 0) && (grub_get_time_ms () - start_time < 200)); + if (actual > 0) + { + grub_netbuff_put (nb, actual); + return nb; + } + grub_netbuff_free (nb); + return NULL; +} + +static struct grub_net_card_driver ubootnet = + { + .name = "ubnet", + .open = card_open, + .close = card_close, + .send = send_card_buffer, + .recv = get_card_packet + }; + +GRUB_MOD_INIT (ubootnet) +{ + int devcount, i; + int nfound = 0; + + devcount = grub_uboot_dev_enum (); + + for (i = 0; i < devcount; i++) + { + struct device_info *devinfo = grub_uboot_dev_get (i); + struct grub_net_card *card; + + if (!(devinfo->type & DEV_TYP_NET)) + continue; + + card = grub_zalloc (sizeof (struct grub_net_card)); + if (!card) + { + grub_print_error (); + return; + } + + /* FIXME: Any way to check this? */ + card->mtu = 1500; + + grub_memcpy (&(card->default_address.mac), &devinfo->di_net.hwaddr, 6); + card->default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + + card->txbufsize = ALIGN_UP (card->mtu, 64) + 256; + card->txbuf = grub_zalloc (card->txbufsize); + if (!card->txbuf) + { + grub_free (card); + grub_print_error (); + continue; + } + + card->data = devinfo; + card->flags = 0; + card->name = grub_xasprintf ("ubnet_%d", ++nfound); + card->idle_poll_delay_ms = 10; + + card->driver = &ubootnet; + grub_net_card_register (card); + } +} + +GRUB_MOD_FINI (ubootnet) +{ + struct grub_net_card *card, *next; + + FOR_NET_CARDS_SAFE (card, next) + if (card->driver && grub_strcmp (card->driver->name, "ubnet") == 0) + grub_net_card_unregister (card); +} -- cgit v1.2.3