summaryrefslogtreecommitdiffstats
path: root/grub-core/net/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'grub-core/net/drivers')
-rw-r--r--grub-core/net/drivers/efi/efinet.c404
-rw-r--r--grub-core/net/drivers/emu/emunet.c116
-rw-r--r--grub-core/net/drivers/i386/pc/pxe.c419
-rw-r--r--grub-core/net/drivers/ieee1275/ofnet.c565
-rw-r--r--grub-core/net/drivers/uboot/ubootnet.c161
5 files changed, 1665 insertions, 0 deletions
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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/net/netbuff.h>
+#include <grub/dl.h>
+#include <grub/net.h>
+#include <grub/time.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/i18n.h>
+
+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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/net/netbuff.h>
+#include <grub/net.h>
+#include <grub/term.h>
+#include <grub/i18n.h>
+#include <grub/emu/net.h>
+
+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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/net.h>
+#include <grub/mm.h>
+#include <grub/file.h>
+#include <grub/misc.h>
+#include <grub/env.h>
+#include <grub/i18n.h>
+#include <grub/loader.h>
+
+#include <grub/machine/pxe.h>
+#include <grub/machine/int.h>
+#include <grub/machine/memory.h>
+#include <grub/machine/kernel.h>
+
+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, &regs);
+
+ 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/net/netbuff.h>
+#include <grub/ieee1275/ieee1275.h>
+#include <grub/dl.h>
+#include <grub/net.h>
+#include <grub/time.h>
+#include <grub/i18n.h>
+
+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 *) &prop;
+ 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/net/netbuff.h>
+#include <grub/uboot/disk.h>
+#include <grub/uboot/uboot.h>
+#include <grub/uboot/api_public.h>
+#include <grub/dl.h>
+#include <grub/net.h>
+#include <grub/time.h>
+#include <grub/i18n.h>
+
+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);
+}