diff --git a/ChangeLog b/ChangeLog
index 1db00bc4d..070461b5e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2010-04-09  BVK Chaitanya  <bvk.groups@gmail.com>
+
+	While and until loops support to GRUB script.
+
+	* include/grub/script_sh.h (grub_script_cmdwhile): New struct.
+	(grub_script_create_cmdwhile): New function prototype.
+	(grub_script_execute_cmdwhile): New function prototype.
+	* script/execute.c (grub_script_execute_cmdwhile): New function.
+	* script/parser.y (command): New commands.
+	(whilecmd): New grammar rule.
+	(untilcmd): New grammar rule.
+	* script/script.c (grub_script_create_cmdwhile): New function.
+	* util/grub-script-check.c (grub_script_execute_cmdwhile): New
+	function.
+
+	* tests/grub_script_while1.in: New testcase.
+	* conf/tests.rmk: Rule for new testcase.
+
 2010-04-09  Vladimir Serbinenko  <phcoder@gmail.com>
 
 	* util/grub.d/00_header.in: Add few missing quotes. Recognise *.jpeg
diff --git a/conf/tests.rmk b/conf/tests.rmk
index ca1f0cd71..5f78ea940 100644
--- a/conf/tests.rmk
+++ b/conf/tests.rmk
@@ -53,6 +53,9 @@ grub_script_vars1_SOURCES = tests/grub_script_vars1.in
 check_SCRIPTS += grub_script_for1
 grub_script_for1_SOURCES = tests/grub_script_for1.in
 
+check_SCRIPTS += grub_script_while1
+grub_script_while1_SOURCES = tests/grub_script_while1.in
+
 check_SCRIPTS += grub_script_blanklines
 grub_script_blanklines_SOURCES = tests/grub_script_blanklines.in
 
@@ -69,6 +72,7 @@ SCRIPTED_TESTS  = grub_script_echo1
 SCRIPTED_TESTS += grub_script_echo_keywords
 SCRIPTED_TESTS += grub_script_vars1
 SCRIPTED_TESTS += grub_script_for1
+SCRIPTED_TESTS += grub_script_while1
 SCRIPTED_TESTS += grub_script_blanklines
 SCRIPTED_TESTS += grub_script_final_semicolon
 
diff --git a/include/grub/script_sh.h b/include/grub/script_sh.h
index 33c6d2b85..b55b6a806 100644
--- a/include/grub/script_sh.h
+++ b/include/grub/script_sh.h
@@ -121,6 +121,21 @@ struct grub_script_cmdfor
   struct grub_script_cmd *list;
 };
 
+/* A while/until command.  */
+struct grub_script_cmdwhile
+{
+  struct grub_script_cmd cmd;
+
+  /* The command list used as condition.  */
+  struct grub_script_cmd *cond;
+
+  /* The command list executed in each loop.  */
+  struct grub_script_cmd *list;
+
+  /* The flag to indicate this as "until" loop.  */
+  int until;
+};
+
 /* A menu entry generate statement.  */
 struct grub_script_cmd_menuentry
 {
@@ -234,6 +249,12 @@ grub_script_create_cmdfor (struct grub_parser_param *state,
 			   struct grub_script_arglist *words,
 			   struct grub_script_cmd *list);
 
+struct grub_script_cmd *
+grub_script_create_cmdwhile (struct grub_parser_param *state,
+			     struct grub_script_cmd *cond,
+			     struct grub_script_cmd *list,
+			     int is_an_until_loop);
+
 struct grub_script_cmd *
 grub_script_create_cmdmenu (struct grub_parser_param *state,
 			    struct grub_script_arglist *arglist,
@@ -283,6 +304,7 @@ grub_err_t grub_script_execute_cmdline (struct grub_script_cmd *cmd);
 grub_err_t grub_script_execute_cmdblock (struct grub_script_cmd *cmd);
 grub_err_t grub_script_execute_cmdif (struct grub_script_cmd *cmd);
 grub_err_t grub_script_execute_cmdfor (struct grub_script_cmd *cmd);
+grub_err_t grub_script_execute_cmdwhile (struct grub_script_cmd *cmd);
 grub_err_t grub_script_execute_menuentry (struct grub_script_cmd *cmd);
 
 /* Execute any GRUB pre-parsed command or script.  */
diff --git a/script/execute.c b/script/execute.c
index 6cd15e87d..40f161267 100644
--- a/script/execute.c
+++ b/script/execute.c
@@ -26,13 +26,24 @@
 #include <grub/lib/arg.h>
 #include <grub/normal.h>
 
+/* Max digits for a char is 3 (0xFF is 255), similarly for an int it
+   is sizeof (int) * 3, and one extra for a possible -ve sign.  */
+#define ERRNO_DIGITS_MAX  (sizeof (int) * 3 + 1)
+
 static grub_err_t
 grub_script_execute_cmd (struct grub_script_cmd *cmd)
 {
+  int ret;
+  char errnobuf[ERRNO_DIGITS_MAX + 1];
+
   if (cmd == 0)
     return 0;
 
-  return cmd->exec (cmd);
+  ret = cmd->exec (cmd);
+
+  grub_snprintf (errnobuf, sizeof (errnobuf), "%d", ret);
+  grub_env_set ("?", errnobuf);
+  return ret;
 }
 
 #define ARG_ALLOCATION_UNIT  (32 * sizeof (char))
@@ -260,13 +271,14 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd)
 grub_err_t
 grub_script_execute_cmdblock (struct grub_script_cmd *cmd)
 {
+  int ret = 0;
   struct grub_script_cmdblock *cmdblock = (struct grub_script_cmdblock *) cmd;
 
   /* Loop over every command and execute it.  */
   for (cmd = cmdblock->cmdlist; cmd; cmd = cmd->next)
-    grub_script_execute_cmd (cmd);
+    ret = grub_script_execute_cmd (cmd);
 
-  return 0;
+  return ret;
 }
 
 /* Execute an if statement.  */
@@ -317,6 +329,26 @@ grub_script_execute_cmdfor (struct grub_script_cmd *cmd)
   return result;
 }
 
+/* Execute a "while" or "until" command.  */
+grub_err_t
+grub_script_execute_cmdwhile (struct grub_script_cmd *cmd)
+{
+  int cond;
+  int result;
+  struct grub_script_cmdwhile *cmdwhile = (struct grub_script_cmdwhile *) cmd;
+
+  result = 0;
+  do {
+    cond = grub_script_execute_cmd (cmdwhile->cond);
+    if (cmdwhile->until ? !cond : cond)
+      break;
+
+    result = grub_script_execute_cmd (cmdwhile->list);
+  } while (1); /* XXX Put a check for ^C here */
+
+  return result;
+}
+
 /* Execute the menu entry generate statement.  */
 grub_err_t
 grub_script_execute_menuentry (struct grub_script_cmd *cmd)
diff --git a/script/parser.y b/script/parser.y
index fdd3f7589..a30dd78b3 100644
--- a/script/parser.y
+++ b/script/parser.y
@@ -74,8 +74,9 @@
 %token <arg> GRUB_PARSER_TOKEN_WORD      "word"
 
 %type <arglist> word argument arguments0 arguments1
-%type <cmd> script_init script grubcmd ifcmd forcmd command
-%type <cmd> commands1 menuentry statement
+%type <cmd> script_init script
+%type <cmd> grubcmd ifcmd forcmd whilecmd untilcmd
+%type <cmd> command commands1 menuentry statement
 
 %pure-parser
 %lex-param   { struct grub_parser_param *state };
@@ -171,9 +172,11 @@ grubcmd: word arguments0
 ;
 
 /* A single command.  */
-command: grubcmd { $$ = $1; }
-       | ifcmd   { $$ = $1; }
-       | forcmd  { $$ = $1; }
+command: grubcmd  { $$ = $1; }
+       | ifcmd    { $$ = $1; }
+       | forcmd   { $$ = $1; }
+       | whilecmd { $$ = $1; }
+       | untilcmd { $$ = $1; }
 ;
 
 /* A list of commands. */
@@ -248,3 +251,25 @@ forcmd: "for" "name"
 	  grub_script_lexer_deref (state->lexerstate);
 	}
 ;
+
+whilecmd: "while"
+          {
+	    grub_script_lexer_ref (state->lexerstate);
+          }
+          commands1 delimiters1 "do" commands1 delimiters1 "done"
+	  {
+	    $$ = grub_script_create_cmdwhile (state, $3, $6, 0);
+	    grub_script_lexer_deref (state->lexerstate);
+	  }
+;
+
+untilcmd: "until"
+          {
+	    grub_script_lexer_ref (state->lexerstate);
+          }
+          commands1 delimiters1 "do" commands1 delimiters1 "done"
+	  {
+	    $$ = grub_script_create_cmdwhile (state, $3, $6, 1);
+	    grub_script_lexer_deref (state->lexerstate);
+	  }
+;
diff --git a/script/script.c b/script/script.c
index 9142a8245..4c87d9491 100644
--- a/script/script.c
+++ b/script/script.c
@@ -245,6 +245,28 @@ grub_script_create_cmdfor (struct grub_parser_param *state,
   return (struct grub_script_cmd *) cmd;
 }
 
+/* Create a "while" or "until" command.  */
+struct grub_script_cmd *
+grub_script_create_cmdwhile (struct grub_parser_param *state,
+			     struct grub_script_cmd *cond,
+			     struct grub_script_cmd *list,
+			     int is_an_until_loop)
+{
+  struct grub_script_cmdwhile *cmd;
+
+  cmd = grub_script_malloc (state, sizeof (*cmd));
+  if (! cmd)
+    return 0;
+
+  cmd->cmd.exec = grub_script_execute_cmdwhile;
+  cmd->cmd.next = 0;
+  cmd->cond = cond;
+  cmd->list = list;
+  cmd->until = is_an_until_loop;
+
+  return (struct grub_script_cmd *) cmd;
+}
+
 /* Create a command that adds a menu entry to the menu.  Title is an
    argument that is parsed to generate a string that can be used as
    the title.  The sourcecode for this entry is passed in SOURCECODE.
diff --git a/tests/grub_script_while1.in b/tests/grub_script_while1.in
new file mode 100644
index 000000000..554247f76
--- /dev/null
+++ b/tests/grub_script_while1.in
@@ -0,0 +1,32 @@
+#! @builddir@/grub-shell-tester
+
+echo one
+foo=""
+while test "$foo" != "1111"; do foo="${foo}1"; echo "$foo"; done
+
+echo two
+foo=""
+while test "$foo" != "aaaa"
+do
+  foo="${foo}a"
+  echo $foo
+done
+
+foo=""
+until test "$foo" = "1111"; do foo="${foo}1"; echo $foo; done
+foo=""
+until test "$foo" = "aaaa"
+do
+  foo="${foo}a"
+  echo $foo
+done
+
+# check "$?" in condition gets its value from while body commands
+foo=""
+false
+while test "$?" != "0"
+do
+    echo $foo
+    foo="${foo}1"
+    test "$foo" = "111111"
+done
diff --git a/util/grub-script-check.c b/util/grub-script-check.c
index 7dc400dde..af39dcdc0 100644
--- a/util/grub-script-check.c
+++ b/util/grub-script-check.c
@@ -87,6 +87,12 @@ grub_script_execute_cmdfor (struct grub_script_cmd *cmd __attribute__ ((unused))
   return 0;
 }
 
+grub_err_t
+grub_script_execute_cmdwhile (struct grub_script_cmd *cmd __attribute__ ((unused)))
+{
+  return 0;
+}
+
 grub_err_t
 grub_script_execute_menuentry (struct grub_script_cmd *cmd __attribute__ ((unused)))
 {