Hi,

This is the new patch, it contain the following changes:

1. command edit -> set in grub-editenv, as it seems more natural.

grub-editenv FILE set name=value ...

2. findroot can locate the root device using filename. To use it, just
set idfile to any file inside the directory, For example:

idfile=normal.mod

Scan preference: uuid > label > idfile

3. Change the order device are reported in biosdisk, so that floppy appear last.

4. Add new command loadenv, which can be used to load/save environment variable.

load_env [-f FILE]
Load environment variable from external environment block file. The
default file is $prefix/grubenv, but you can overwrite it with -f
parameter.

list_env  [-f FILE]
List the variables in external environment block file.

save_env [-f FILE] variable_name ..
Save the value of variable_name to external environment block file

So, to archive the effect of savedefault command in grub legacy, you can use:

load_env
..

menuentry aa {
..
save_env default
}

You can also use save_env to save the variable directly to core.img,
but it's not recommended. The environment block inside core.img is
quite small, it's mainly used to store critical variable like rdir,
uuid, label and idfile.

-- 
Bean
diff --git a/boot/i386/pc/lnxboot.S b/boot/i386/pc/lnxboot.S
index 955cc41..380ef08 100644
--- a/boot/i386/pc/lnxboot.S
+++ b/boot/i386/pc/lnxboot.S
@@ -185,7 +185,7 @@ real_code_2:
 	call	move_memory
 
 	/* Check for multiboot signature.  */
-	cmpl	$MULTIBOOT_MAGIC, %ss:(DATA_ADDR + 0x50)
+	cmpl	$MULTIBOOT_MAGIC, %ss:(DATA_ADDR + GRUB_KERNEL_MACHINE_DATA_END)
 	jz	1f
 
 	movl	(ramdisk_image - start), %esi
diff --git a/commands/loadenv.c b/commands/loadenv.c
new file mode 100755
index 0000000..970baf3
--- /dev/null
+++ b/commands/loadenv.c
@@ -0,0 +1,250 @@
+/* loadenv.c - command to load/save environment variable.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/normal.h>
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/arg.h>
+#include <grub/file.h>
+#include <grub/disk.h>
+#include <grub/misc.h>
+#include <grub/env.h>
+#include <grub/envblk.h>
+
+static const struct grub_arg_option options[] =
+  {
+    {"file", 'f', 0, "specify filename", 0, ARG_TYPE_PATHNAME},
+    {0, 0, 0, 0, 0, 0}
+  };
+
+char buffer[GRUB_ENVBLK_MAXLEN];
+grub_envblk_t envblk;
+
+static grub_file_t
+read_envblk_file (char *filename, void NESTED_FUNC_ATTR read_hook (grub_disk_addr_t sector, unsigned offset, unsigned length))
+{
+  char *buf = 0;
+  grub_file_t file;
+
+  if (! filename)
+    {
+      char *prefix;
+
+      prefix = grub_env_get ("prefix");
+      if (prefix)
+        {
+          int len;
+
+          len = grub_strlen (prefix);
+          buf = grub_malloc (len + 1 + sizeof (GRUB_ENVBLK_DEFCFG));
+          grub_strcpy (buf, prefix);
+          buf[len] = '/';
+          grub_strcpy (buf + len + 1, GRUB_ENVBLK_DEFCFG);
+          filename = buf;
+        }
+      else
+        {
+          grub_error (GRUB_ERR_FILE_NOT_FOUND, "prefix is not found");
+          return 0;
+        }
+    }
+
+  file = grub_file_open (filename);
+  grub_free (buf);
+  if (! file)
+    return 0;
+
+  if (read_hook)
+    {
+      if (! file->device->disk)
+        {
+          grub_file_close (file);
+          grub_error (GRUB_ERR_BAD_DEVICE,
+                      "this command is available only for disk devices.");
+          return 0;
+        }
+      file->read_hook = read_hook;
+    }
+
+  if (grub_file_read (file, buffer, GRUB_ENVBLK_MAXLEN) != GRUB_ENVBLK_MAXLEN)
+    {
+      grub_file_close (file);
+      grub_error (GRUB_ERR_BAD_FILE_TYPE, "file too short");
+      return 0;
+    }
+
+  envblk = grub_envblk_find (buffer);
+  if (! envblk)
+    {
+      grub_file_close (file);
+      grub_error (GRUB_ERR_BAD_FILE_TYPE, "environment block not found");
+      return 0;
+    }
+
+  return file;
+}
+
+static grub_err_t
+grub_cmd_load_env (struct grub_arg_list *state,
+                   int argc __attribute__ ((unused)), char **args __attribute__ ((unused)))
+
+{
+  grub_file_t file;
+
+  auto int hook (char *name, char *value);
+  int hook (char *name, char *value)
+    {
+      grub_env_set (name, value);
+
+      return 0;
+    }
+
+  file = read_envblk_file ((state[0].set) ? state[0].arg : 0, 0);
+  if (! file)
+    return grub_errno;
+
+  grub_file_close (file);
+
+  grub_envblk_iterate (envblk, hook);
+
+  return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_list_env (struct grub_arg_list *state,
+                   int argc __attribute__ ((unused)), char **args __attribute__ ((unused)))
+{
+  grub_file_t file;
+
+  auto int hook (char *name, char *value);
+  int hook (char *name, char *value)
+    {
+      grub_printf ("%s=%s\n", name, value);
+
+      return 0;
+    }
+
+  file = read_envblk_file ((state[0].set) ? state[0].arg : 0, 0);
+  if (! file)
+    return grub_errno;
+
+  grub_file_close (file);
+
+  grub_envblk_iterate (envblk, hook);
+
+  return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_save_env (struct grub_arg_list *state, int argc, char **args)
+{
+  grub_file_t file;
+  grub_disk_t disk;
+  grub_disk_addr_t addr[GRUB_ENVBLK_MAXLEN >> GRUB_DISK_SECTOR_BITS];
+  char buf[GRUB_DISK_SECTOR_SIZE];
+  int num = 0;
+
+  auto void NESTED_FUNC_ATTR hook (grub_disk_addr_t sector, unsigned offset,
+                                   unsigned length);
+
+  void NESTED_FUNC_ATTR hook (grub_disk_addr_t sector,
+                              unsigned offset, unsigned length)
+    {
+      if ((offset != 0) || (length != GRUB_DISK_SECTOR_SIZE))
+        return;
+
+      if (num < (GRUB_ENVBLK_MAXLEN >> GRUB_DISK_SECTOR_BITS))
+        addr[num++] = sector;
+    }
+
+  if (! argc)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "No variable is specified");
+
+  file = read_envblk_file ((state[0].set) ? state[0].arg : 0, hook);
+  if (! file)
+    return grub_errno;
+
+  file->read_hook = 0;
+
+  if (num != GRUB_ENVBLK_MAXLEN >> GRUB_DISK_SECTOR_BITS)
+    {
+      grub_error (GRUB_ERR_BAD_DEVICE, "invalid blocklist");
+      goto quit;
+    }
+
+  disk = file->device->disk;
+  for (num = 0; num < (GRUB_ENVBLK_MAXLEN >> GRUB_DISK_SECTOR_BITS); num++)
+    {
+      if (disk->dev->read (disk, addr[num], 1, buf))
+        goto quit;
+
+      if (grub_memcmp (&buffer[num << GRUB_DISK_SECTOR_BITS], buf,
+                       GRUB_DISK_SECTOR_SIZE))
+        {
+          grub_error (GRUB_ERR_BAD_DEVICE, "invalid blocklist");
+          goto quit;
+        }
+    }
+
+  while (argc)
+    {
+      char *value;
+
+      value = grub_env_get (args[0]);
+      if (value)
+        {
+          if (grub_envblk_insert (envblk, args[0], value))
+            {
+              grub_error (GRUB_ERR_BAD_ARGUMENT, "environment block too small");
+              goto quit;
+            }
+        }
+
+      argc--;
+      args++;
+    }
+
+  for (num = 0; num < (GRUB_ENVBLK_MAXLEN >> GRUB_DISK_SECTOR_BITS); num++)
+    if (disk->dev->write (disk, addr[num], 1,
+                          &buffer[num << GRUB_DISK_SECTOR_BITS]))
+      goto quit;
+
+quit:
+  grub_file_close (file);
+
+  return grub_errno;
+}
+
+GRUB_MOD_INIT(loadenv)
+{
+  (void) mod;
+  grub_register_command ("load_env", grub_cmd_load_env, GRUB_COMMAND_FLAG_BOTH,
+			 "load_env [-f FILE]", "Load variables from environment block file.", options);
+  grub_register_command ("list_env", grub_cmd_list_env, GRUB_COMMAND_FLAG_BOTH,
+			 "list_env [-f FILE]", "List variables from environment block file.", options);
+  grub_register_command ("save_env", grub_cmd_save_env, GRUB_COMMAND_FLAG_BOTH,
+			 "save_env [-f FILE] variable_name [...]", "Save variables to environment block file.", options);
+}
+
+GRUB_MOD_FINI(loadenv)
+{
+  grub_unregister_command ("load_env");
+  grub_unregister_command ("list_env");
+  grub_unregister_command ("save_env");
+}
diff --git a/conf/common.rmk b/conf/common.rmk
index acbebc7..99252ac 100644
--- a/conf/common.rmk
+++ b/conf/common.rmk
@@ -94,6 +94,11 @@ grub_fstest_init.c: grub_fstest_init.lst $(filter-out grub_fstest_init.c,$(grub_
 	rm -f $@; sh $(srcdir)/geninit.sh $< $(filter %.c,$^) > $@
 DISTCLEANFILES += grub_fstest_init.c
 
+# for grub-editenv
+bin_UTILITIES += grub-editenv
+grub_editenv_SOURCES = util/grub-editenv.c util/envblk.c util/misc.c
+CLEANFILES += grub-editenv
+
 # For update-grub
 update-grub: util/update-grub.in config.status
 	./config.status --file=$@:$<
@@ -282,7 +287,7 @@ pkglib_MODULES += hello.mod boot.mod terminal.mod ls.mod	\
 	cmp.mod cat.mod help.mod font.mod search.mod		\
 	loopback.mod configfile.mod echo.mod			\
 	terminfo.mod test.mod blocklist.mod hexdump.mod		\
-	read.mod sleep.mod
+	read.mod sleep.mod loadenv.mod
 
 # For hello.mod.
 hello_mod_SOURCES = hello/hello.c
@@ -364,8 +369,23 @@ hexdump_mod_SOURCES = commands/hexdump.c
 hexdump_mod_CFLAGS = $(COMMON_CFLAGS)
 hexdump_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+# For read.mod.
+read_mod_SOURCES = commands/read.c
+read_mod_CFLAGS = $(COMMON_CFLAGS)
+read_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For sleep.mod.
+sleep_mod_SOURCES = commands/sleep.c
+sleep_mod_CFLAGS = $(COMMON_CFLAGS)
+sleep_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For loadenv.mod.
+loadenv_mod_SOURCES = commands/loadenv.c util/envblk.c
+loadenv_mod_CFLAGS = $(COMMON_CFLAGS)
+loadenv_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
 # Misc.
-pkglib_MODULES += gzio.mod elf.mod
+pkglib_MODULES += gzio.mod elf.mod findroot.mod
 
 # For elf.mod.
 elf_mod_SOURCES = kern/elf.c
@@ -377,12 +397,7 @@ gzio_mod_SOURCES = io/gzio.c
 gzio_mod_CFLAGS = $(COMMON_CFLAGS)
 gzio_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
-# For read.mod.
-read_mod_SOURCES = commands/read.c
-read_mod_CFLAGS = $(COMMON_CFLAGS)
-read_mod_LDFLAGS = $(COMMON_LDFLAGS)
-
-# For sleep.mod.
-sleep_mod_SOURCES = commands/sleep.c
-sleep_mod_CFLAGS = $(COMMON_CFLAGS)
-sleep_mod_LDFLAGS = $(COMMON_LDFLAGS)
+# For findroot.mod.
+findroot_mod_SOURCES = kern/findroot.c
+findroot_mod_CFLAGS = $(COMMON_CFLAGS)
+findroot_mod_LDFLAGS = $(COMMON_LDFLAGS)
diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c
index 094bde0..6e5292f 100644
--- a/disk/i386/pc/biosdisk.c
+++ b/disk/i386/pc/biosdisk.c
@@ -66,12 +66,6 @@ grub_biosdisk_iterate (int (*hook) (const char *name))
   int drive;
   int num_floppies;
 
-  /* For floppy disks, we can get the number safely.  */
-  num_floppies = grub_biosdisk_get_num_floppies ();
-  for (drive = 0; drive < num_floppies; drive++)
-    if (grub_biosdisk_call_hook (hook, drive))
-      return 1;
-  
   /* For hard disks, attempt to read the MBR.  */
   for (drive = 0x80; drive < 0x90; drive++)
     {
@@ -92,6 +86,12 @@ grub_biosdisk_iterate (int (*hook) (const char *name))
       return 1;
     }
 
+  /* For floppy disks, we can get the number safely.  */
+  num_floppies = grub_biosdisk_get_num_floppies ();
+  for (drive = 0; drive < num_floppies; drive++)
+    if (grub_biosdisk_call_hook (hook, drive))
+      return 1;
+
   return 0;
 }
 
diff --git a/include/grub/envblk.h b/include/grub/envblk.h
new file mode 100755
index 0000000..e075089
--- /dev/null
+++ b/include/grub/envblk.h
@@ -0,0 +1,51 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_ENVBLK_HEADER
+#define GRUB_ENVBLK_HEADER	1
+
+#define GRUB_ENVBLK_SIGNATURE	0x4b627645	/* EvbK  */
+
+#define GRUB_ENVBLK_MAXLEN	8192
+
+/* Names of important environment variables.  */
+#define GRUB_ENVBLK_RDIR	"rdir"
+#define GRUB_ENVBLK_UUID	"uuid"
+#define GRUB_ENVBLK_LABEL	"label"
+#define GRUB_ENVBLK_IDFILE	"idfile"
+
+#define GRUB_ENVBLK_DEFCFG	"grubenv"
+
+#ifndef ASM_FILE
+
+struct grub_envblk
+{
+  grub_uint32_t signature;
+  grub_uint16_t length;
+  char data[0];
+} __attribute__ ((packed));
+typedef struct grub_envblk *grub_envblk_t;
+
+grub_envblk_t grub_envblk_find (char *buf);
+int grub_envblk_insert (grub_envblk_t envblk, char *name, char *value);
+void grub_envblk_delete (grub_envblk_t envblk, char *name);
+void grub_envblk_iterate (grub_envblk_t envblk, int hook (char *name, char *value));
+
+#endif
+
+#endif /* ! GRUB_ENVBLK_HEADER */
diff --git a/include/grub/i386/pc/kernel.h b/include/grub/i386/pc/kernel.h
index 43a8d5b..13548c5 100644
--- a/include/grub/i386/pc/kernel.h
+++ b/include/grub/i386/pc/kernel.h
@@ -37,14 +37,14 @@
 /* The offset of GRUB_MEMDISK_IMAGE_SIZE.  */
 #define GRUB_KERNEL_MACHINE_MEMDISK_IMAGE_SIZE	0x1c
 
-/* The offset of GRUB_PREFIX.  */
-#define GRUB_KERNEL_MACHINE_PREFIX		0x20
+/* The offset of GRUB_ENVBLK.  */
+#define GRUB_KERNEL_MACHINE_ENVBLK		0x26
 
 /* End of the data section. */
-#define GRUB_KERNEL_MACHINE_DATA_END		0x50
+#define GRUB_KERNEL_MACHINE_DATA_END		0x70
 
 /* The size of the first region which won't be compressed.  */
-#define GRUB_KERNEL_MACHINE_RAW_SIZE		0x4A0
+#define GRUB_KERNEL_MACHINE_RAW_SIZE		0x4C0
 
 #ifndef ASM_FILE
 
@@ -66,9 +66,9 @@ extern grub_int32_t grub_install_bsd_part;
 /* The size of memory disk image, if present.  */
 extern grub_int32_t grub_memdisk_image_size;
 
-/* The prefix which points to the directory where GRUB modules and its
-   configuration file are located.  */
-extern char grub_prefix[];
+/* The envblk contains variable which can be used to locate the directory where
+   GRUB modules and its configuration file.  */
+extern char grub_envblk[];
 
 /* The boot BIOS drive number.  */
 extern grub_int32_t EXPORT_VAR(grub_boot_drive);
diff --git a/include/grub/kernel.h b/include/grub/kernel.h
index 4a4e2cc..49f3516 100644
--- a/include/grub/kernel.h
+++ b/include/grub/kernel.h
@@ -55,8 +55,10 @@ void grub_machine_init (void);
 /* The machine-specific finalization.  */
 void grub_machine_fini (void);
 
-/* The machine-specific prefix initialization.  */
-void grub_machine_set_prefix (void);
+/* The machine-specific root initialization.  */
+void grub_machine_set_root (void);
+
+char *grub_machine_get_envblk (void);
 
 /* Register all the exported symbols. This is automatically generated.  */
 void grub_register_exported_symbols (void);
diff --git a/kern/findroot.c b/kern/findroot.c
new file mode 100755
index 0000000..44d5eb8
--- /dev/null
+++ b/kern/findroot.c
@@ -0,0 +1,110 @@
+/* findroot.c - search for root device */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/device.h>
+#include <grub/file.h>
+#include <grub/env.h>
+#include <grub/machine/kernel.h>
+#include <grub/envblk.h>
+
+static void
+findroot (void)
+{
+  char *label, *uuid, *idfile, *rdir;
+  int found = 0;
+
+  auto int iterate_rdir (const char *filename, int dir);
+  int iterate_rdir (const char *filename, int dir)
+    {
+      (void) dir;
+
+      found = (! grub_strcmp (filename, idfile));
+      return found;
+    }
+
+  auto int iterate_device (const char *name);
+  int iterate_device (const char *name)
+    {
+      grub_device_t dev;
+      grub_fs_t fs;
+
+      dev = grub_device_open (name);
+      if (dev)
+        {
+          fs = grub_fs_probe (dev);
+          if (fs)
+            {
+              if ((uuid) && (fs->uuid))
+                {
+                  char *cur;
+
+                  fs->uuid (dev, &cur);
+                  if (cur)
+                    {
+                      found = (! grub_strcmp (uuid, cur));
+                      grub_free (cur);
+                    }
+                }
+              else if ((label) && (fs->label))
+                {
+                  char *cur;
+
+                  fs->label (dev, &cur);
+                  if (cur)
+                    {
+                      found = (! grub_strcmp (label, cur));
+                      grub_free (cur);
+                    }
+                }
+              else if (idfile)
+                fs->dir (dev, rdir, iterate_rdir);
+
+              if (found)
+                grub_env_set ("root", name);
+            }
+
+          grub_device_close (dev);
+        }
+
+      grub_errno = GRUB_ERR_NONE;
+      return found;
+    }
+
+  uuid = grub_env_get (GRUB_ENVBLK_UUID);
+  label = grub_env_get (GRUB_ENVBLK_LABEL);
+  idfile = grub_env_get (GRUB_ENVBLK_IDFILE);
+  rdir = grub_env_get (GRUB_ENVBLK_RDIR);
+  if (! rdir)
+    rdir = "/";
+
+  if ((! label) && (! uuid) && (! idfile))
+    return;
+
+  grub_device_iterate (iterate_device);
+}
+
+GRUB_MOD_INIT(findroot)
+{
+  findroot ();
+}
diff --git a/kern/i386/pc/init.c b/kern/i386/pc/init.c
index 757f5d5..aabdff2 100644
--- a/kern/i386/pc/init.c
+++ b/kern/i386/pc/init.c
@@ -58,40 +58,6 @@ grub_arch_sync_caches (void *address __attribute__ ((unused)),
 {
 }
 
-static char *
-make_install_device (void)
-{
-  /* XXX: This should be enough.  */
-  char dev[100];
-
-  if (grub_memdisk_image_size)
-    {
-      grub_sprintf (dev, "(memdisk)%s", grub_prefix);
-      grub_strcpy (grub_prefix, dev);
-    }
-  else if (grub_install_dos_part != -2)
-    {
-      /* If the root drive is not set explicitly, assume that it is identical
-         to the boot drive.  */
-      if (grub_root_drive == 0xFF)
-        grub_root_drive = grub_boot_drive;
-      
-      grub_sprintf (dev, "(%cd%u", (grub_root_drive & 0x80) ? 'h' : 'f',
-                    grub_root_drive & 0x7f);
-      
-      if (grub_install_dos_part >= 0)
-	grub_sprintf (dev + grub_strlen (dev), ",%u", grub_install_dos_part + 1);
-      
-      if (grub_install_bsd_part >= 0)
-	grub_sprintf (dev + grub_strlen (dev), ",%c", grub_install_bsd_part + 'a');
-      
-      grub_sprintf (dev + grub_strlen (dev), ")%s", grub_prefix);
-      grub_strcpy (grub_prefix, dev);
-    }
-      
-  return grub_prefix;
-}
-
 /* Add a memory region.  */
 static void
 add_mem_region (grub_addr_t addr, grub_size_t size)
@@ -238,11 +204,40 @@ grub_machine_init (void)
     grub_fatal ("no upper memory");
 }
 
+char *
+grub_machine_get_envblk (void)
+{
+  return grub_envblk;
+}
+
 void
-grub_machine_set_prefix (void)
+grub_machine_set_root (void)
 {
-  /* Initialize the prefix.  */
-  grub_env_set ("prefix", make_install_device ());
+  /* XXX: This should be enough.  */
+  char dev[100];
+
+  if (grub_memdisk_image_size)
+    {
+      grub_env_set ("root", "memdisk");
+    }
+  else if (grub_install_dos_part != -2)
+    {
+      /* If the root drive is not set explicitly, assume that it is identical
+         to the boot drive.  */
+      if (grub_root_drive == 0xFF)
+        grub_root_drive = grub_boot_drive;
+
+      grub_sprintf (dev, "%cd%u", (grub_root_drive & 0x80) ? 'h' : 'f',
+                    grub_root_drive & 0x7f);
+
+      if (grub_install_dos_part >= 0)
+	grub_sprintf (dev + grub_strlen (dev), ",%u", grub_install_dos_part + 1);
+
+      if (grub_install_bsd_part >= 0)
+	grub_sprintf (dev + grub_strlen (dev), ",%c", grub_install_bsd_part + 'a');
+
+      grub_env_set ("root", dev);
+    }
 }
 
 void
diff --git a/kern/i386/pc/startup.S b/kern/i386/pc/startup.S
index ebb98fe..cba710a 100644
--- a/kern/i386/pc/startup.S
+++ b/kern/i386/pc/startup.S
@@ -52,6 +52,7 @@
 #include <grub/term.h>
 #include <multiboot.h>
 #include <multiboot2.h>
+#include <grub/envblk.h>
 		
 #define ABS(x)	((x) - EXT_C(start) + GRUB_BOOT_MACHINE_KERNEL_ADDR + 0x200)
 	
@@ -98,14 +99,19 @@ VARIABLE(grub_install_bsd_part)
 	.long	0xFFFFFFFF
 VARIABLE(grub_memdisk_image_size)
 	.long	0
-VARIABLE(grub_prefix)
+	.long	GRUB_ENVBLK_SIGNATURE
+	.word	envblk_end - grub_envblk
+VARIABLE(grub_envblk)
+	.byte	0
 	/* to be filled by grub-mkimage */
 
 	/*
 	 *  Leave some breathing room for the prefix.
 	 */
 
-	. = EXT_C(start) + 0x50
+	. = EXT_C(start) + 0x70
+
+envblk_end:
 
 /*
  * Support for booting GRUB from a Multiboot boot loader (e.g. GRUB itself).
diff --git a/kern/main.c b/kern/main.c
index 09de03a..729dd78 100644
--- a/kern/main.c
+++ b/kern/main.c
@@ -28,6 +28,33 @@
 #include <grub/device.h>
 #include <grub/env.h>
 
+static void
+grub_parse_envblk (void)
+{
+  char *env;
+
+  env = grub_machine_get_envblk ();
+  if (! env)
+    return;
+
+  while (*env)
+    {
+      char *value;
+
+      value = grub_strchr (env, '=');
+      if (value)
+        {
+          *(value++) = 0;
+          grub_env_set (env, value);
+          env = value;
+        }
+      else
+        grub_env_set (env, "");
+
+      env += grub_strlen (env) + 1;
+    }
+}
+
 /* Load all modules in core.  */
 static void
 grub_load_modules (void)
@@ -75,24 +102,28 @@ grub_env_write_root (struct grub_env_var *var __attribute__ ((unused)),
 static void
 grub_set_root_dev (void)
 {
-  const char *prefix;
+  char *root, *rdir, *prefix;
+
+  if (! grub_env_get ("root"))
+    grub_machine_set_root ();
 
   grub_register_variable_hook ("root", 0, grub_env_write_root);
   grub_env_export ("root");
   
-  prefix = grub_env_get ("prefix");
+  root = grub_env_get ("root");
+  if (! *root)
+    return;
   
-  if (prefix)
-    {
-      char *dev;
-
-      dev = grub_file_get_device_name (prefix);
-      if (dev)
-	{
-	  grub_env_set ("root", dev);
-	  grub_free (dev);
-	}
-    }
+  rdir = grub_env_get ("rdir");
+  if (! rdir)
+    rdir = "/";
+
+  prefix = grub_malloc (grub_strlen (root) + grub_strlen (rdir) + 3);
+  grub_sprintf (prefix, "(%s)%s", root, rdir);
+
+  grub_env_set ("prefix", prefix);
+
+  grub_free (prefix);
 }
 
 /* Load the normal mode module and execute the normal mode if possible.  */
@@ -118,13 +149,15 @@ grub_main (void)
   grub_printf ("Welcome to GRUB!\n\n");
   grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
 
+  /* Parse the environment block.  */
+  grub_parse_envblk ();
+
   /* Load pre-loaded modules and free the space.  */
   grub_register_exported_symbols ();
   grub_load_modules ();
 
   /* It is better to set the root device as soon as possible,
      for convenience.  */
-  grub_machine_set_prefix ();
   grub_set_root_dev ();
 
   /* Load the normal mode module.  */
diff --git a/util/envblk.c b/util/envblk.c
new file mode 100755
index 0000000..5b27062
--- /dev/null
+++ b/util/envblk.c
@@ -0,0 +1,171 @@
+/* envblk.c - Common function for environment block.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <grub/types.h>
+#include <grub/envblk.h>
+
+#ifdef GRUB_UTIL
+
+#include <string.h>
+
+#define grub_strlen	strlen
+#define grub_strcpy	strcpy
+#define grub_strchr	strchr
+#define grub_memcmp	memcmp
+#define grub_memcpy	memcpy
+
+#else
+
+#include <grub/misc.h>
+
+#endif
+
+grub_envblk_t
+grub_envblk_find (char *buf)
+{
+  grub_uint32_t *pd;
+  int len;
+
+  pd = (grub_uint32_t *) buf;
+
+  for (len = GRUB_ENVBLK_MAXLEN - 6; len > 0; len -= 4, pd++)
+    if (*pd == GRUB_ENVBLK_SIGNATURE)
+      {
+        grub_envblk_t p;
+
+        p = (grub_envblk_t) pd;
+        if (p->length <= len)
+          return p;
+      }
+
+  return 0;
+}
+
+int
+grub_envblk_insert (grub_envblk_t envblk, char *name, char *value)
+{
+  char *p, *pend;
+  char *found = 0;
+  int nl;
+
+  nl = grub_strlen (name);
+  p = envblk->data;
+  pend = p + envblk->length;
+
+  while (*p)
+    {
+      if ((! found) && (! grub_memcmp (name, p, nl)) && (p[nl] == '='))
+        found = p + nl + 1;
+
+      p += grub_strlen (p) + 1;
+      if (p >= pend)
+        return 1;
+    }
+
+  if (found)
+    {
+      int len1, len2;
+
+      len1 = grub_strlen (found);
+      len2 = grub_strlen (value);
+      if ((p - envblk->data) + 1 - len1 + len2 > envblk->length)
+        return 1;
+
+      grub_memcpy (found + len2 + 1, found + len1 + 1, (p - found) - len1);
+      grub_strcpy (found, value);
+    }
+  else
+    {
+      int len2 = grub_strlen (value);
+
+      if ((p - envblk->data) + nl + 1 + len2 + 2 > envblk->length)
+        return 1;
+
+      grub_strcpy (p, name);
+      p[nl] = '=';
+      grub_strcpy (p + nl + 1, value);
+      p[nl + 1 + len2 + 1] = 0;
+    }
+
+  return 0;
+}
+
+void
+grub_envblk_delete (grub_envblk_t envblk, char *name)
+{
+  char *p, *pend;
+  char *found = 0;
+  int nl;
+
+  nl = grub_strlen (name);
+  p = envblk->data;
+  pend = p + envblk->length;
+
+  while (*p)
+    {
+      if ((! found) && (! grub_memcmp (name, p, nl)) && (p[nl] == '='))
+        found = p;
+
+      p += grub_strlen (p) + 1;
+      if (p >= pend)
+        return;
+    }
+
+  if (found)
+    {
+      int len;
+
+      len = grub_strlen (found);
+      grub_memcpy (found, found + len + 1, (p - found) - len);
+    }
+}
+
+void
+grub_envblk_iterate (grub_envblk_t envblk,
+                     int hook (char *name, char *value))
+{
+  char *p, *pend;
+
+  p = envblk->data;
+  pend = p + envblk->length;
+
+  while (*p)
+    {
+      char *v;
+      int r;
+
+      v = grub_strchr (p, '=');
+      if (v)
+        {
+          *v = 0;
+          r = hook (p, v + 1);
+          *v = '=';
+        }
+      else
+        r = hook (p, "");
+
+      if (r)
+        break;
+
+      p += grub_strlen (p) + 1;
+      if (p >= pend)
+        break;
+    }
+}
diff --git a/util/grub-editenv.c b/util/grub-editenv.c
new file mode 100755
index 0000000..8c08694
--- /dev/null
+++ b/util/grub-editenv.c
@@ -0,0 +1,228 @@
+/* grub-editenv.c - tool to edit environment block.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <grub/types.h>
+#include <grub/util/misc.h>
+
+#include <grub/envblk.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+static struct option options[] = {
+  {"help", no_argument, 0, 'h'},
+  {"version", no_argument, 0, 'V'},
+  {"verbose", no_argument, 0, 'v'},
+  {0, 0, 0, 0}
+};
+
+char buffer[GRUB_ENVBLK_MAXLEN];
+grub_envblk_t envblk;
+
+static void
+usage (int status)
+{
+  if (status)
+    fprintf (stderr, "Try ``grub-editenv --help'' for more information.\n");
+  else
+    printf ("\
+Usage: grub-editenv [OPTIONS] FILENAME COMMAND\n\
+\n\
+Tool to edit environment block.\n\
+\nCommands:\n\
+  create                    create a blank environment block file\n\
+  info                      show information about the environment block\n\
+  list                      list the current variables\n\
+  set [name=value] ...      change/delete variables\n\
+  clear                     delete all variables\n\
+\nOptions:\n\
+  -h, --help                display this message and exit\n\
+  -V, --version             print version information and exit\n\
+  -v, --verbose             print verbose messages\n\
+\n\
+Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
+
+  exit (status);
+}
+
+int
+create_envblk_file (char *name)
+{
+  FILE *f;
+  grub_envblk_t p;
+
+  f = fopen (name, "wb");
+  if (! f)
+    return 1;
+
+  /* Just in case OS don't save 0s.  */
+  memset (buffer, -1, sizeof (buffer));
+
+  p = (grub_envblk_t) &buffer[0];
+  p->signature = GRUB_ENVBLK_SIGNATURE;
+  p->length = sizeof (buffer) - sizeof (struct grub_envblk);
+  p->data[0] = p->data[1] = 0;
+
+  fwrite (buffer, sizeof (buffer), 1, f);
+
+  fclose (f);
+  return 0;
+}
+
+FILE *
+open_envblk_file (char *name)
+{
+  FILE *f;
+
+  f = fopen (name, "r+b");
+  if (! f)
+    grub_util_error ("Can\'t open file %s", name);
+
+  if (fread (buffer, 1, sizeof (buffer), f) != sizeof (buffer))
+    grub_util_error ("The envblk file is too short");
+
+  envblk = grub_envblk_find (buffer);
+  if (! envblk)
+    grub_util_error ("Can\'t find environment block");
+
+  return f;
+}
+
+static void
+cmd_info (void)
+{
+  printf ("Envblk offset: %d\n", envblk->data - buffer);
+  printf ("Envblk length: %d\n", envblk->length);
+}
+
+static void
+cmd_list (void)
+{
+  auto int hook (char *name, char *value);
+  int hook (char *name, char *value)
+    {
+      printf ("%s=%s\n", name, value);
+      return 0;
+    }
+
+  grub_envblk_iterate (envblk, hook);
+}
+
+static void
+cmd_set (int argc, char *argv[])
+{
+  while (argc)
+    {
+      char *p;
+
+      p = strchr (argv[0], '=');
+      if (! p)
+        grub_util_error ("Invalid parameter");
+
+      *(p++) = 0;
+
+      if (*p)
+        {
+          if (grub_envblk_insert (envblk, argv[0], p))
+            grub_util_error ("Environment block too small");
+        }
+      else
+        grub_envblk_delete (envblk, argv[0]);
+
+      argc--;
+      argv++;
+    }
+}
+
+static void
+cmd_clear (void)
+{
+  envblk->data[0] = envblk->data[1] = 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+  FILE *f;
+
+  progname = "grub-editenv";
+
+  /* Check for options.  */
+  while (1)
+    {
+      int c = getopt_long (argc, argv, "hVv", options, 0);
+
+      if (c == -1)
+	break;
+      else
+	switch (c)
+	  {
+	  case 'h':
+	    usage (0);
+	    break;
+
+	  case 'V':
+	    printf ("%s (%s) %s\n", progname, PACKAGE_NAME, PACKAGE_VERSION);
+	    return 0;
+
+	  case 'v':
+	    verbosity++;
+	    break;
+
+	  default:
+	    usage (1);
+	    break;
+	  }
+    }
+
+  /* Obtain PATH.  */
+  if (optind + 1 >= argc)
+    {
+      fprintf (stderr, "Not enough parameter.\n");
+      usage (1);
+    }
+
+  if (! strcmp (argv[optind + 1], "create"))
+    return create_envblk_file (argv[optind]);
+
+  f = open_envblk_file (argv[optind]);
+
+  optind++;
+  if (! strcmp (argv[optind], "info"))
+    cmd_info ();
+  else if (! strcmp (argv[optind], "list"))
+    cmd_list ();
+  else
+    {
+      if (! strcmp (argv[optind], "set"))
+        cmd_set (argc - optind - 1, argv + optind + 1);
+      else if (! strcmp (argv[optind], "clear"))
+        cmd_clear ();
+
+      fseek (f, 0, SEEK_SET);
+      fwrite (buffer, sizeof (buffer), 1, f);
+    }
+  fclose (f);
+
+  return 0;
+}
diff --git a/util/grub-emu.c b/util/grub-emu.c
index 00a2c49..aa7ceb9 100644
--- a/util/grub-emu.c
+++ b/util/grub-emu.c
@@ -36,15 +36,13 @@
 #include <grub/util/getroot.h>
 #include <grub/env.h>
 #include <grub/partition.h>
+#include <grub/envblk.h>
 
 #include <grub_emu_init.h>
 
 /* Used for going back to the main function.  */
 jmp_buf main_env;
 
-/* Store the prefix specified by an argument.  */
-static char *prefix = 0;
-
 grub_addr_t
 grub_arch_modules_addr (void)
 {
@@ -76,11 +74,15 @@ grub_machine_init (void)
 }
 
 void
-grub_machine_set_prefix (void)
+grub_machine_set_root (void)
 {
-  grub_env_set ("prefix", prefix);
-  free (prefix);
-  prefix = 0;
+  /* Do nothing, as root is already set.  */
+}
+
+char *
+grub_machine_get_envblk (void)
+{
+  return 0;
 }
 
 void
@@ -205,8 +207,8 @@ main (int argc, char *argv[])
     }
 
   dir = grub_get_prefix (dir);
-  prefix = xmalloc (strlen (root_dev) + 2 + strlen (dir) + 1);
-  sprintf (prefix, "(%s)%s", root_dev, dir);
+  grub_env_set (GRUB_ENVBLK_RDIR, dir);
+  grub_env_set ("root", root_dev);
   free (dir);
   
   /* Start GRUB!  */
diff --git a/util/i386/pc/grub-mkimage.c b/util/i386/pc/grub-mkimage.c
index 48d6dfc..f71924b 100644
--- a/util/i386/pc/grub-mkimage.c
+++ b/util/i386/pc/grub-mkimage.c
@@ -27,6 +27,7 @@
 #include <grub/util/misc.h>
 #include <grub/util/resolve.h>
 #include <grub/misc.h>
+#include <grub/envblk.h>
 
 #include <stdio.h>
 #include <unistd.h>
@@ -109,9 +110,10 @@ generate_image (const char *dir, char *prefix, FILE *out, char *mods[], char *me
   kernel_img = xmalloc (kernel_size + total_module_size + memdisk_size);
   grub_util_load_image (kernel_path, kernel_img);
 
-  if (GRUB_KERNEL_MACHINE_PREFIX + strlen (prefix) + 1 > GRUB_KERNEL_MACHINE_DATA_END)
+  if (GRUB_KERNEL_MACHINE_ENVBLK + sizeof (GRUB_ENVBLK_RDIR) + 2 + strlen (prefix) > GRUB_KERNEL_MACHINE_DATA_END)
     grub_util_error ("prefix too long");
-  strcpy (kernel_img + GRUB_KERNEL_MACHINE_PREFIX, prefix);
+  strcpy (kernel_img + GRUB_KERNEL_MACHINE_ENVBLK, GRUB_ENVBLK_RDIR "=");
+  strcpy (kernel_img + GRUB_KERNEL_MACHINE_ENVBLK + sizeof (GRUB_ENVBLK_RDIR), prefix);
 
   /* Fill in the grub_module_info structure.  */
   modinfo = (struct grub_module_info *) (kernel_img + kernel_size);
diff --git a/util/i386/pc/grub-setup.c b/util/i386/pc/grub-setup.c
index 535a8d0..2a243e8 100644
--- a/util/i386/pc/grub-setup.c
+++ b/util/i386/pc/grub-setup.c
@@ -34,6 +34,7 @@
 #include <grub/term.h>
 #include <grub/util/raid.h>
 #include <grub/util/lvm.h>
+#include <grub/envblk.h>
 
 static const grub_gpt_part_type_t grub_gpt_partition_type_bios_boot = GRUB_GPT_PARTITION_TYPE_BIOS_BOOT;
 
@@ -235,7 +236,10 @@ setup (const char *prefix, const char *dir,
   install_bsd_part = (grub_int32_t *) (core_img + GRUB_DISK_SECTOR_SIZE
 				       + GRUB_KERNEL_MACHINE_INSTALL_BSD_PART);
   install_prefix = (core_img + GRUB_DISK_SECTOR_SIZE
-		    + GRUB_KERNEL_MACHINE_PREFIX);
+		    + GRUB_KERNEL_MACHINE_ENVBLK);
+
+  strcpy (install_prefix, GRUB_ENVBLK_RDIR "=");
+  install_prefix += sizeof (GRUB_ENVBLK_RDIR);
 
   /* Open the root device and the destination device.  */
   root_dev = grub_device_open (root);
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to