123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549 |
- /*
- * 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;
- struct grub_net_buff *nb;
- rc = grub_ieee1275_read (data->handle, dev->rcvbuf, dev->rcvbufsize, &actual);
- if (actual <= 0 || rc < 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_clz (~grub_be_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;
- 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;
- 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;
- }
|