/* pxe.c - command to control the pxe driver */
/*
* GRUB -- GRand Unified Bootloader
- * Copyright (C) 2008 Free Software Foundation, Inc.
+ * Copyright (C) 2008,2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <grub/err.h>
#include <grub/misc.h>
#include <grub/machine/pxe.h>
-#include <grub/extcmd.h>
-
-static const struct grub_arg_option options[] =
-{
- {"info", 'i', 0, "show PXE information.", 0, 0},
- {"bsize", 'b', 0, "set PXE block size", 0, ARG_TYPE_INT},
- {"unload", 'u', 0, "unload PXE stack.", 0, 0},
- {0, 0, 0, 0, 0, 0}
- };
-
-static void
-print_ip (grub_uint32_t ip)
-{
- int i;
-
- for (i = 0; i < 3; i++)
- {
- grub_printf ("%d.", ip & 0xFF);
- ip >>= 8;
- }
- grub_printf ("%d", ip);
-}
+#include <grub/command.h>
static grub_err_t
-grub_cmd_pxe (grub_extcmd_t cmd, int argc __attribute__ ((unused)),
- char **args __attribute__ ((unused)))
+grub_cmd_pxe_unload (grub_command_t cmd __attribute__ ((unused)),
+ int argc __attribute__ ((unused)),
+ char **args __attribute__ ((unused)))
{
- struct grub_arg_list *state = cmd->state;
-
if (! grub_pxe_pxenv)
return grub_error (GRUB_ERR_FILE_NOT_FOUND, "no pxe environment");
- if (state[1].set)
- {
- int size;
-
- size = grub_strtoul (state[1].arg, 0, 0);
- if (size < GRUB_PXE_MIN_BLKSIZE)
- size = GRUB_PXE_MIN_BLKSIZE;
- else if (size > GRUB_PXE_MAX_BLKSIZE)
- size = GRUB_PXE_MAX_BLKSIZE;
-
- grub_pxe_blksize = size;
- }
-
- if (state[0].set)
- {
- grub_printf ("blksize : %d\n", grub_pxe_blksize);
- grub_printf ("client ip : ");
- print_ip (grub_pxe_your_ip);
- grub_printf ("\nserver ip : ");
- print_ip (grub_pxe_server_ip);
- grub_printf ("\ngateway ip : ");
- print_ip (grub_pxe_gateway_ip);
- grub_printf ("\n");
- }
-
- if (state[2].set)
- grub_pxe_unload ();
+ grub_pxe_unload ();
return 0;
}
-static grub_extcmd_t cmd;
+static grub_command_t cmd;
GRUB_MOD_INIT(pxecmd)
{
- cmd = grub_register_extcmd ("pxe", grub_cmd_pxe, GRUB_COMMAND_FLAG_BOTH,
- "pxe [-i|-b|-u]",
- "Command to control the PXE device.", options);
+ cmd = grub_register_command ("pxe_unload", grub_cmd_pxe_unload,
+ "pxe_unload",
+ "Unload PXE environment.");
}
GRUB_MOD_FINI(pxecmd)
{
- grub_unregister_extcmd (cmd);
+ grub_unregister_command (cmd);
}
/* pxe.c - Driver to provide access to the pxe filesystem */
/*
* GRUB -- GRand Unified Bootloader
- * Copyright (C) 2008 Free Software Foundation, Inc.
+ * Copyright (C) 2008,2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <grub/file.h>
#include <grub/misc.h>
#include <grub/bufio.h>
+#include <grub/env.h>
#include <grub/machine/pxe.h>
#include <grub/machine/memory.h>
#define SEGOFS(x) ((SEGMENT(x) << 16) + OFFSET(x))
#define LINEAR(x) (void *) (((x >> 16) <<4) + (x & 0xFFFF))
+struct grub_pxe_disk_data
+{
+ grub_uint32_t server_ip;
+ grub_uint32_t gateway_ip;
+};
+
struct grub_pxenv *grub_pxe_pxenv;
-grub_uint32_t grub_pxe_your_ip;
-grub_uint32_t grub_pxe_server_ip;
-grub_uint32_t grub_pxe_gateway_ip;
-int grub_pxe_blksize = GRUB_PXE_MIN_BLKSIZE;
+static grub_uint32_t grub_pxe_your_ip;
+static grub_uint32_t grub_pxe_default_server_ip;
+static grub_uint32_t grub_pxe_default_gateway_ip;
+static unsigned grub_pxe_blksize = GRUB_PXE_MIN_BLKSIZE;
static grub_file_t curr_file = 0;
return 0;
}
+static grub_err_t
+parse_ip (const char *val, grub_uint32_t *ip, const char **rest)
+{
+ grub_uint32_t newip = 0;
+ unsigned long t;
+ int i;
+ const char *ptr = val;
+
+ for (i = 0; i < 4; i++)
+ {
+ t = grub_strtoul (ptr, (char **) &ptr, 0);
+ if (grub_errno)
+ return grub_errno;
+ if (t & ~0xff)
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid IP.");
+ newip >>= 8;
+ newip |= (t << 24);
+ if (i != 3 && *ptr != '.')
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid IP.");
+ ptr++;
+ }
+ *ip = newip;
+ if (rest)
+ *rest = ptr - 1;
+ return 0;
+}
+
static grub_err_t
grub_pxe_open (const char *name, grub_disk_t disk)
{
- if (grub_strcmp (name, "pxe"))
- return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a pxe disk");
+ struct grub_pxe_disk_data *data;
+
+ if (grub_strcmp (name, "pxe") != 0
+ && grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) != 0)
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a pxe disk");
+
+ data = grub_malloc (sizeof (*data));
+ if (!data)
+ return grub_errno;
+
+ if (grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) == 0)
+ {
+ const char *ptr;
+ grub_err_t err;
+
+ ptr = name + sizeof ("pxe:") - 1;
+ err = parse_ip (ptr, &(data->server_ip), &ptr);
+ if (err)
+ return err;
+ if (*ptr == ':')
+ {
+ err = parse_ip (ptr + 1, &(data->server_ip), 0);
+ if (err)
+ return err;
+ }
+ else
+ data->gateway_ip = grub_pxe_default_gateway_ip;
+ }
+ else
+ {
+ data->server_ip = grub_pxe_default_server_ip;
+ data->gateway_ip = grub_pxe_default_gateway_ip;
+ }
disk->total_sectors = 0;
- disk->id = (unsigned long) "pxe";
+ disk->id = (unsigned long) data;
disk->has_partitions = 0;
- disk->data = 0;
+ disk->data = data;
return GRUB_ERR_NONE;
}
static void
-grub_pxe_close (grub_disk_t disk __attribute((unused)))
+grub_pxe_close (grub_disk_t disk)
{
+ grub_free (disk->data);
}
static grub_err_t
struct grub_pxenv_tftp_open c2;
} c;
struct grub_pxe_data *data;
+ struct grub_pxe_disk_data *disk_data = file->device->disk->data;
grub_file_t file_int, bufio;
if (curr_file != 0)
curr_file = 0;
}
- c.c1.server_ip = grub_pxe_server_ip;
- c.c1.gateway_ip = grub_pxe_gateway_ip;
+ c.c1.server_ip = disk_data->server_ip;
+ c.c1.gateway_ip = disk_data->gateway_ip;
grub_strcpy ((char *)&c.c1.filename[0], name);
grub_pxe_call (GRUB_PXENV_TFTP_GET_FSIZE, &c.c1);
if (c.c1.status)
{
struct grub_pxenv_tftp_read c;
struct grub_pxe_data *data;
+ struct grub_pxe_disk_data *disk_data = file->device->disk->data;
grub_uint32_t pn, r;
data = file->data;
if (curr_file != 0)
grub_pxe_call (GRUB_PXENV_TFTP_CLOSE, &o);
- o.server_ip = grub_pxe_server_ip;
- o.gateway_ip = grub_pxe_gateway_ip;
+ o.server_ip = disk_data->server_ip;
+ o.gateway_ip = disk_data->gateway_ip;
grub_strcpy ((char *)&o.filename[0], data->filename);
o.tftp_port = grub_cpu_to_be16 (GRUB_PXE_TFTP_PORT);
o.packet_size = grub_pxe_blksize;
.next = 0
};
+static char *
+grub_env_write_readonly (struct grub_env_var *var __attribute__ ((unused)),
+ const char *val __attribute__ ((unused)))
+{
+ return NULL;
+}
+
+static void
+set_mac_env (grub_uint8_t *mac_addr, grub_size_t mac_len)
+{
+ char buf[(sizeof ("XX:") - 1) * mac_len + 1];
+ char *ptr = buf;
+ unsigned i;
+
+ for (i = 0; i < mac_len; i++)
+ {
+ grub_sprintf (ptr, "%02x:", mac_addr[i] & 0xff);
+ ptr += (sizeof ("XX:") - 1);
+ }
+ if (mac_len)
+ *(ptr - 1) = 0;
+ else
+ buf[0] = 0;
+
+ grub_env_set ("net_pxe_mac", buf);
+ /* XXX: Is it possible to change MAC in PXE? */
+ grub_register_variable_hook ("net_pxe_mac", 0, grub_env_write_readonly);
+}
+
+static void
+set_env_limn_ro (const char *varname, char *value, grub_size_t len)
+{
+ char c;
+ c = value[len];
+ value[len] = 0;
+ grub_env_set (varname, value);
+ value[len] = c;
+ grub_register_variable_hook (varname, 0, grub_env_write_readonly);
+}
+
+static void
+parse_dhcp_vendor (void *vend, int limit)
+{
+ grub_uint8_t *ptr, *ptr0;
+
+ ptr = ptr0 = vend;
+
+ if (grub_be_to_cpu32 (*(grub_uint32_t *) ptr) != 0x63825363)
+ return;
+ ptr = ptr + sizeof (grub_uint32_t);
+ while (ptr - ptr0 < limit)
+ {
+ grub_uint8_t tagtype;
+ grub_uint8_t taglength;
+
+ tagtype = *ptr++;
+
+ /* Pad tag. */
+ if (tagtype == 0)
+ continue;
+
+ /* End tag. */
+ if (tagtype == 0xff)
+ return;
+
+ taglength = *ptr++;
+
+ switch (tagtype)
+ {
+ case 12:
+ set_env_limn_ro ("net_pxe_hostname", (char *) ptr, taglength);
+ break;
+
+ case 15:
+ set_env_limn_ro ("net_pxe_domain", (char *) ptr, taglength);
+ break;
+
+ case 17:
+ set_env_limn_ro ("net_pxe_rootpath", (char *) ptr, taglength);
+ break;
+
+ case 18:
+ set_env_limn_ro ("net_pxe_extensionspath", (char *) ptr, taglength);
+ break;
+
+ /* If you need any other options please contact GRUB
+ developpement team. */
+ }
+
+ ptr += taglength;
+ }
+}
+
static void
grub_pxe_detect (void)
{
bp = LINEAR (ci.buffer);
grub_pxe_your_ip = bp->your_ip;
- grub_pxe_server_ip = bp->server_ip;
- grub_pxe_gateway_ip = bp->gateway_ip;
-
+ grub_pxe_default_server_ip = bp->server_ip;
+ grub_pxe_default_gateway_ip = bp->gateway_ip;
+ set_mac_env (bp->mac_addr, bp->hw_len < sizeof (bp->mac_addr) ? bp->hw_len
+ : sizeof (bp->mac_addr));
+ set_env_limn_ro ("net_pxe_boot_file", (char *) bp->boot_file,
+ sizeof (bp->boot_file));
+ set_env_limn_ro ("net_pxe_dhcp_server_name", (char *) bp->server_name,
+ sizeof (bp->server_name));
+ parse_dhcp_vendor (&bp->vendor, sizeof (bp->vendor));
grub_pxe_pxenv = pxenv;
}
}
}
+static void
+set_ip_env (char *varname, grub_uint32_t ip)
+{
+ char buf[sizeof ("XXX.XXX.XXX.XXX")];
+
+ grub_sprintf (buf, "%d.%d.%d.%d", (ip & 0xff),
+ (ip >> 8) & 0xff, (ip >> 16) & 0xff, (ip >> 24) & 0xff);
+ grub_env_set (varname, buf);
+}
+
+static char *
+write_ip_env (grub_uint32_t *ip, const char *val)
+{
+ char *buf;
+ grub_err_t err;
+ grub_uint32_t newip;
+
+ err = parse_ip (val, &newip, 0);
+ if (err)
+ return 0;
+
+ /* Normalize the IP. */
+ buf = grub_malloc (sizeof ("XXX.XXX.XXX.XXX"));
+ if (!buf)
+ return 0;
+
+ *ip = newip;
+
+ grub_sprintf (buf, "%d.%d.%d.%d", (newip & 0xff), (newip >> 8) & 0xff,
+ (newip >> 16) & 0xff, (newip >> 24) & 0xff);
+
+ return buf;
+}
+
+static char *
+grub_env_write_pxe_default_server (struct grub_env_var *var
+ __attribute__ ((unused)),
+ const char *val)
+{
+ return write_ip_env (&grub_pxe_default_server_ip, val);
+}
+
+static char *
+grub_env_write_pxe_default_gateway (struct grub_env_var *var
+ __attribute__ ((unused)),
+ const char *val)
+{
+ return write_ip_env (&grub_pxe_default_gateway_ip, val);
+}
+
+static char *
+grub_env_write_pxe_blocksize (struct grub_env_var *var __attribute__ ((unused)),
+ const char *val)
+{
+ unsigned size;
+ char *buf;
+
+ size = grub_strtoul (val, 0, 0);
+ if (grub_errno)
+ return 0;
+
+ if (size < GRUB_PXE_MIN_BLKSIZE)
+ size = GRUB_PXE_MIN_BLKSIZE;
+ else if (size > GRUB_PXE_MAX_BLKSIZE)
+ size = GRUB_PXE_MAX_BLKSIZE;
+
+ buf = grub_malloc (sizeof ("XXXXXX XXXXXX"));
+ if (!buf)
+ return 0;
+
+ grub_sprintf (buf, "%d", size);
+ grub_pxe_blksize = size;
+
+ return buf;
+}
+
+
GRUB_MOD_INIT(pxe)
{
grub_pxe_detect ();
if (grub_pxe_pxenv)
{
+ char *buf;
+
+ buf = grub_malloc (sizeof ("XXXXXX XXXXXX"));
+ if (buf)
+ {
+ grub_sprintf (buf, "%d", grub_pxe_blksize);
+ grub_env_set ("net_pxe_blksize", buf);
+ }
+
+ set_ip_env ("pxe_default_server", grub_pxe_default_server_ip);
+ set_ip_env ("pxe_default_gateway", grub_pxe_default_gateway_ip);
+ set_ip_env ("net_pxe_ip", grub_pxe_your_ip);
+ grub_register_variable_hook ("net_pxe_default_server", 0,
+ grub_env_write_pxe_default_server);
+ grub_register_variable_hook ("net_pxe_default_gateway", 0,
+ grub_env_write_pxe_default_gateway);
+
+ /* XXX: Is it possible to change IP in PXE? */
+ grub_register_variable_hook ("net_pxe_ip", 0,
+ grub_env_write_readonly);
+ grub_register_variable_hook ("net_pxe_blksize", 0,
+ grub_env_write_pxe_blocksize);
grub_disk_dev_register (&grub_pxe_dev);
grub_fs_register (&grub_pxefs_fs);
}