[1992] 2009-02-14 Christian Franke <[email protected]>

Christian Franke <[email protected]>
Newsgroups gmane.comp.boot-loaders.grub.cvs
Message-ID <[email protected]>
Revision: 1992
          http://svn.sv.gnu.org/viewvc/?view=rev&root=grub&revision=1992
Author:   chrfranke
Date:     2009-02-14 12:57:55 +0000 (Sat, 14 Feb 2009)

Log Message:
-----------
2009-02-14  Christian Franke  <[email protected]>

	* commands/hdparm.c: New file.  Provides `hdparm' command
	which sends ATA commands via grub_disk_ata_pass_through ().

	* conf/i386-pc.rmk: Add ata_pthru.mod and hdparm.mod.

	* disk/ata.c: Include <grub/ata.h>.  Move <grub/misc.h>
	and <grub/cpu/io.h> to include/grub/ata.h.
	(enum grub_ata_addressing_t): Move to include/grub/ata.h.
	(GRUB_CDROM_SECTOR_SIZE): Remove.
	(GRUB_ATA_*): Move to include/grub/ata.h.
	(GRUB_ATAPI_*): Likewise.
	(enum grub_ata_commands): Likewise.
	(enum grub_ata_timeout_milliseconds): Likewise.
	(struct grub_ata_device): Likewise.
	(grub_ata_regset): Likewise.
	(grub_ata_regget): Likewise.
	(grub_ata_regset2): Likewise.
	(grub_ata_regget2): Likewise.
	(grub_ata_check_ready): Likewise.
	(grub_ata_wait_not_busy): Remove static, exported in
	include/grub/ata.h.
	(grub_ata_wait_drq): Likewise.
	(grub_ata_pio_read): Likewise.

	* disk/ata_pthru.c: New file.  Provides grub_ata_pass_through ()
	function for hdparm.mod.

	* include/grub/ata.h: New file, contains declarations from
	disk/ata.c.
	(enum grub_ata_commands): Add new commands for commands/hdparm.c.

	* include/grub/disk.h (grub_disk_ata_pass_through_parms): New struct.
	(grub_disk_ata_pass_through): New exported variable.

	* kern/disk.c (grub_disk_ata_pass_through): New variable.

Modified Paths:
--------------
    trunk/grub2/ChangeLog
    trunk/grub2/DISTLIST
    trunk/grub2/conf/i386-pc.mk
    trunk/grub2/conf/i386-pc.rmk
    trunk/grub2/disk/ata.c
    trunk/grub2/include/grub/disk.h
    trunk/grub2/kern/disk.c

Added Paths:
-----------
    trunk/grub2/commands/hdparm.c
    trunk/grub2/disk/ata_pthru.c
    trunk/grub2/include/grub/ata.h

Modified: trunk/grub2/ChangeLog
===================================================================
--- trunk/grub2/ChangeLog	2009-02-13 20:26:56 UTC (rev 1991)
+++ trunk/grub2/ChangeLog	2009-02-14 12:57:55 UTC (rev 1992)
@@ -1,3 +1,41 @@
+2009-02-14  Christian Franke  <[email protected]>
+
+	* commands/hdparm.c: New file.  Provides `hdparm' command
+	which sends ATA commands via grub_disk_ata_pass_through ().
+
+	* conf/i386-pc.rmk: Add ata_pthru.mod and hdparm.mod.
+
+	* disk/ata.c: Include <grub/ata.h>.  Move <grub/misc.h>
+	and <grub/cpu/io.h> to include/grub/ata.h.
+	(enum grub_ata_addressing_t): Move to include/grub/ata.h.
+	(GRUB_CDROM_SECTOR_SIZE): Remove.
+	(GRUB_ATA_*): Move to include/grub/ata.h.
+	(GRUB_ATAPI_*): Likewise.
+	(enum grub_ata_commands): Likewise.
+	(enum grub_ata_timeout_milliseconds): Likewise.
+	(struct grub_ata_device): Likewise.
+	(grub_ata_regset): Likewise.
+	(grub_ata_regget): Likewise.
+	(grub_ata_regset2): Likewise.
+	(grub_ata_regget2): Likewise.
+	(grub_ata_check_ready): Likewise.
+	(grub_ata_wait_not_busy): Remove static, exported in
+	include/grub/ata.h.
+	(grub_ata_wait_drq): Likewise.
+	(grub_ata_pio_read): Likewise.
+
+	* disk/ata_pthru.c: New file.  Provides grub_ata_pass_through ()
+	function for hdparm.mod.
+
+	* include/grub/ata.h: New file, contains declarations from
+	disk/ata.c.
+	(enum grub_ata_commands): Add new commands for commands/hdparm.c.
+
+	* include/grub/disk.h (grub_disk_ata_pass_through_parms): New struct.
+	(grub_disk_ata_pass_through): New exported variable.
+
+	* kern/disk.c (grub_disk_ata_pass_through): New variable.
+
 2009-02-13  Colin D Bennett  <[email protected]>
 
 	Support multiple fallback entries, and provide an API to support

Modified: trunk/grub2/DISTLIST
===================================================================
--- trunk/grub2/DISTLIST	2009-02-13 20:26:56 UTC (rev 1991)
+++ trunk/grub2/DISTLIST	2009-02-14 12:57:55 UTC (rev 1992)
@@ -49,6 +49,7 @@
 commands/date.c
 commands/echo.c
 commands/halt.c
+commands/hdparm.c
 commands/help.c
 commands/hexdump.c
 commands/loadenv.c
@@ -90,6 +91,7 @@
 conf/x86_64-efi.mk
 conf/x86_64-efi.rmk
 disk/ata.c
+disk/ata_pthru.c
 disk/dmraid_nvidia.c
 disk/fs_uuid.c
 disk/host.c
@@ -139,6 +141,7 @@
 include/grub/acorn_filecore.h
 include/grub/aout.h
 include/grub/arg.h
+include/grub/ata.h
 include/grub/bitmap.h
 include/grub/boot.h
 include/grub/bufio.h

Added: trunk/grub2/commands/hdparm.c
===================================================================
--- trunk/grub2/commands/hdparm.c	                        (rev 0)
+++ trunk/grub2/commands/hdparm.c	2009-02-14 12:57:55 UTC (rev 1992)
@@ -0,0 +1,421 @@
+/* hdparm.c - command to get/set ATA disk parameters.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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
+ *  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/arg.h>
+#include <grub/ata.h>
+#include <grub/disk.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/normal.h>
+#include <grub/mm.h>
+#include <grub/lib/hexdump.h>
+
+
+static const struct grub_arg_option options[] = {
+  {"apm",             'B', 0, "set Advanced Power Management\n"
+			      "(1=low, ..., 254=high, 255=off)",
+			      0, ARG_TYPE_INT},
+  {"power",           'C', 0, "check power mode", 0, ARG_TYPE_NONE},
+  {"security-freeze", 'F', 0, "freeze ATA security settings until reset",
+			      0, ARG_TYPE_NONE},
+  {"health",          'H', 0, "check SMART health status", 0, ARG_TYPE_NONE},
+  {"aam",             'M', 0, "set Automatic Acoustic Management\n"
+			      "(0=off, 128=quiet, ..., 254=fast)",
+			      0, ARG_TYPE_INT},
+  {"standby-timeout", 'S', 0, "set standby timeout\n"
+			      "(0=off, 1=5s, 2=10s, ..., 240=20m, 241=30m, ...)",
+			      0, ARG_TYPE_INT},
+  {"standby",         'y', 0, "set drive to standby mode", 0, ARG_TYPE_NONE},
+  {"sleep",           'Y', 0, "set drive to sleep mode", 0, ARG_TYPE_NONE},
+  {"identify",        'i', 0, "print drive identity and settings",
+			      0, ARG_TYPE_NONE},
+  {"dumpid",          'I', 0, "dump contents of ATA IDENTIFY sector",
+			       0, ARG_TYPE_NONE},
+  {"smart",            -1, 0, "disable/enable SMART (0/1)", 0, ARG_TYPE_INT},
+  {"quiet",           'q', 0, "do not print messages", 0, ARG_TYPE_NONE},
+  {0, 0, 0, 0, 0, 0}
+};
+
+enum grub_ata_smart_commands
+  {
+    GRUB_ATA_FEAT_SMART_ENABLE  = 0xd8,
+    GRUB_ATA_FEAT_SMART_DISABLE = 0xd9,
+    GRUB_ATA_FEAT_SMART_STATUS  = 0xda,
+  };
+
+static int quiet = 0;
+
+static grub_err_t
+grub_hdparm_do_ata_cmd (grub_disk_t disk, grub_uint8_t cmd,
+			grub_uint8_t features, grub_uint8_t sectors,
+			void * buffer, int size)
+{
+  struct grub_disk_ata_pass_through_parms apt;
+  grub_memset (&apt, 0, sizeof (apt));
+
+  apt.taskfile[GRUB_ATA_REG_CMD] = cmd;
+  apt.taskfile[GRUB_ATA_REG_FEATURES] = features;
+  apt.taskfile[GRUB_ATA_REG_SECTORS] = sectors;
+  apt.buffer = buffer;
+  apt.size = size;
+
+  if (grub_disk_ata_pass_through (disk, &apt))
+    return grub_errno;
+
+  return GRUB_ERR_NONE;
+}
+
+static int
+grub_hdparm_do_check_powermode_cmd (grub_disk_t disk)
+{
+  struct grub_disk_ata_pass_through_parms apt;
+  grub_memset (&apt, 0, sizeof (apt));
+
+  apt.taskfile[GRUB_ATA_REG_CMD] = GRUB_ATA_CMD_CHECK_POWER_MODE;
+
+  if (grub_disk_ata_pass_through (disk, &apt))
+    return -1;
+
+  return apt.taskfile[GRUB_ATA_REG_SECTORS];
+}
+
+static int
+grub_hdparm_do_smart_cmd (grub_disk_t disk, grub_uint8_t features)
+{
+  struct grub_disk_ata_pass_through_parms apt;
+  grub_memset (&apt, 0, sizeof (apt));
+
+  apt.taskfile[GRUB_ATA_REG_CMD] = GRUB_ATA_CMD_SMART;
+  apt.taskfile[GRUB_ATA_REG_FEATURES] = features;
+  apt.taskfile[GRUB_ATA_REG_LBAMID]  = 0x4f;
+  apt.taskfile[GRUB_ATA_REG_LBAHIGH] = 0xc2;
+
+  if (grub_disk_ata_pass_through (disk, &apt))
+    return -1;
+
+  if (features == GRUB_ATA_FEAT_SMART_STATUS)
+    {
+      if (   apt.taskfile[GRUB_ATA_REG_LBAMID]  == 0x4f
+          && apt.taskfile[GRUB_ATA_REG_LBAHIGH] == 0xc2)
+	return 0; /* Good SMART status.  */
+      else if (   apt.taskfile[GRUB_ATA_REG_LBAMID]  == 0xf4
+	       && apt.taskfile[GRUB_ATA_REG_LBAHIGH] == 0x2c)
+	return 1; /* Bad SMART status.  */
+      else
+	return -1;
+    }
+  return 0;
+}
+
+static grub_err_t
+grub_hdparm_simple_cmd (const char * msg,
+			grub_disk_t disk, grub_uint8_t cmd)
+{
+  if (! quiet && msg)
+    grub_printf ("%s", msg);
+
+  grub_err_t err = grub_hdparm_do_ata_cmd (disk, cmd, 0, 0, NULL, 0);
+
+  if (! quiet && msg)
+    grub_printf ("%s\n", ! err ? "" : ": not supported");
+  return err;
+}
+
+static grub_err_t
+grub_hdparm_set_val_cmd (const char * msg, int val,
+			 grub_disk_t disk, grub_uint8_t cmd,
+			 grub_uint8_t features, grub_uint8_t sectors)
+{
+  if (! quiet && msg && *msg)
+    {
+      if (val >= 0)
+	grub_printf ("Set %s to %d", msg, val);
+      else
+	grub_printf ("Disable %s", msg);
+    }
+
+  grub_err_t err = grub_hdparm_do_ata_cmd (disk, cmd, features, sectors,
+					   NULL, 0);
+
+  if (! quiet && msg)
+    grub_printf ("%s\n", ! err ? "" : ": not supported");
+  return err;
+}
+
+static const char *
+le16_to_char (char *dest, const grub_uint16_t * src16, unsigned bytes)
+{
+  grub_uint16_t * dest16 = (grub_uint16_t *) dest;
+  unsigned i;
+  for (i = 0; i < bytes / 2; i++)
+    dest16[i] = grub_be_to_cpu16 (src16[i]);
+  return dest;
+}
+
+static void
+grub_hdparm_print_identify (const char * idbuf)
+{
+  const grub_uint16_t * idw = (const grub_uint16_t *) idbuf;
+
+  /* Print identity strings.  */
+  char tmp[40];
+  grub_printf ("Model:    \"%.40s\"\n", le16_to_char (tmp, &idw[27], 40));
+  grub_printf ("Firmware: \"%.8s\"\n",  le16_to_char (tmp, &idw[23], 8));
+  grub_printf ("Serial:   \"%.20s\"\n", le16_to_char (tmp, &idw[10], 20));
+
+  /* Print AAM, APM and SMART settings.  */
+  grub_uint16_t features1 = grub_le_to_cpu16 (idw[82]);
+  grub_uint16_t features2 = grub_le_to_cpu16 (idw[83]);
+  grub_uint16_t enabled1  = grub_le_to_cpu16 (idw[85]);
+  grub_uint16_t enabled2  = grub_le_to_cpu16 (idw[86]);
+
+  grub_printf ("Automatic Acoustic Management: ");
+  if (features2 & 0x0200)
+    {
+      if (enabled2 & 0x0200)
+	{
+	  grub_uint16_t aam = grub_le_to_cpu16 (idw[94]);
+	  grub_printf ("%u (128=quiet, ..., 254=fast, recommended=%u)\n",
+		       aam & 0xff, (aam >> 8) & 0xff);
+	}
+      else
+	grub_printf ("disabled\n");
+    }
+  else
+    grub_printf ("not supported\n");
+
+  grub_printf ("Advanced Power Management: ");
+  if (features2 & 0x0008)
+    {
+      if (enabled2 & 0x0008)
+	grub_printf ("%u (1=low, ..., 254=high)\n",
+		     grub_le_to_cpu16 (idw[91]) & 0xff);
+      else
+	grub_printf ("disabled\n");
+    }
+  else
+    grub_printf ("not supported\n");
+
+  grub_printf ("SMART Feature Set: ");
+  if (features1 & 0x0001)
+    grub_printf ("%sabled\n", (enabled1 & 0x0001 ? "en" : "dis"));
+  else
+    grub_printf ("not supported\n");
+
+  /* Print security settings.  */
+  grub_uint16_t security = grub_le_to_cpu16 (idw[128]);
+
+  grub_printf ("ATA Security: ");
+  if (security & 0x0001)
+    grub_printf ("%s, %s, %s, %s\n",
+		 (security & 0x0002 ? "ENABLED" : "disabled"),
+		 (security & 0x0004 ? "**LOCKED**"  : "not locked"),
+		 (security & 0x0008 ? "frozen" : "NOT FROZEN"),
+		 (security & 0x0010 ? "COUNT EXPIRED" : "count not expired"));
+  else
+    grub_printf ("not supported\n");
+}
+
+static void
+grub_hdparm_print_standby_tout (int timeout)
+{
+  if (timeout == 0)
+    grub_printf ("off");
+  else if (timeout <= 252 || timeout == 255)
+    {
+      int h = 0, m = 0 , s = 0;
+      if (timeout == 255)
+	{
+	  m = 21;
+	  s = 15;
+	}
+      else if (timeout == 252)
+	m = 21;
+      else if (timeout <= 240)
+	{
+	  s = timeout * 5;
+	  m = s / 60;
+	  s %= 60;
+	}
+      else
+	{
+	  m = (timeout - 240) * 30;
+	  h  = m / 60;
+	  m %= 60;
+	}
+      grub_printf ("%02d:%02d:%02d", h, m, s);
+    }
+  else
+    grub_printf ("invalid or vendor-specific");
+}
+
+static int get_int_arg (const struct grub_arg_list *state)
+{
+  return (state->set ? (int)grub_strtoul (state->arg, 0, 0) : -1);
+}
+
+
+static grub_err_t
+grub_cmd_hdparm (struct grub_arg_list *state, int argc, char **args) // state????
+{
+  /* Check command line.  */
+  if (argc != 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing device name argument");
+
+  grub_size_t len = grub_strlen (args[0]);
+  if (! (args[0][0] == '(' && args[0][len - 1] == ')'))
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "argument is not a device name");
+  args[0][len - 1] = 0;
+
+  if (! grub_disk_ata_pass_through)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "ATA pass through not available");
+
+  int i = 0;
+  int apm          = get_int_arg (&state[i++]);
+  int power        = state[i++].set;
+  int sec_freeze   = state[i++].set;
+  int health       = state[i++].set;
+  int aam          = get_int_arg (&state[i++]);
+  int standby_tout = get_int_arg (&state[i++]);
+  int standby_now  = state[i++].set;
+  int sleep_now    = state[i++].set;
+  int ident        = state[i++].set;
+  int dumpid       = state[i++].set;
+  int enable_smart = get_int_arg (&state[i++]);
+  quiet            = state[i++].set;
+
+  /* Open disk.  */
+  grub_disk_t disk = grub_disk_open (&args[0][1]);
+  if (! disk)
+    return grub_errno;
+
+  if (disk->partition)
+    {
+      grub_disk_close (disk);
+      return grub_error (GRUB_ERR_BAD_ARGUMENT, "partition not allowed");
+    }
+
+  /* Change settings.  */
+  if (aam >= 0)
+    grub_hdparm_set_val_cmd ("Automatic Acoustic Management", (aam ? aam : -1),
+      disk, GRUB_ATA_CMD_SET_FEATURES, (aam ? 0x42 : 0xc2), aam);
+
+  if (apm >= 0)
+    grub_hdparm_set_val_cmd ("Advanced Power Management",
+      (apm != 255 ? apm : -1), disk, GRUB_ATA_CMD_SET_FEATURES,
+      (apm != 255 ? 0x05 : 0x85), (apm != 255 ? apm : 0));
+
+  if (standby_tout >= 0)
+    {
+      if (! quiet)
+	{
+	  grub_printf ("Set standby timeout to %d (", standby_tout);
+	  grub_hdparm_print_standby_tout (standby_tout);
+	  grub_printf (")");
+	}
+      /* The IDLE cmd sets disk to idle mode and configures standby timer.  */
+      grub_hdparm_set_val_cmd ("", -1, disk, GRUB_ATA_CMD_IDLE, 0, standby_tout);
+    }
+
+  if (enable_smart >= 0)
+    {
+      if (! quiet)
+	grub_printf ("%sable SMART operations", (enable_smart ? "En" : "Dis"));
+      int err = grub_hdparm_do_smart_cmd (disk, (enable_smart ?
+	          GRUB_ATA_FEAT_SMART_ENABLE : GRUB_ATA_FEAT_SMART_DISABLE));
+      if (! quiet)
+	grub_printf ("%s\n", err ? ": not supported" : "");
+    }
+
+  if (sec_freeze)
+    grub_hdparm_simple_cmd ("Freeze security settings", disk,
+                            GRUB_ATA_CMD_SECURITY_FREEZE_LOCK);
+
+  /* Print/dump IDENTIFY.  */
+  if (ident || dumpid)
+    {
+      char buf[GRUB_DISK_SECTOR_SIZE];
+      if (grub_hdparm_do_ata_cmd (disk, GRUB_ATA_CMD_IDENTIFY_DEVICE,
+          0, 0, buf, sizeof (buf)))
+	grub_printf ("Cannot read ATA IDENTIFY data\n");
+      else
+	{
+	  if (ident)
+	    grub_hdparm_print_identify (buf);
+	  if (dumpid)
+	    hexdump (0, buf, sizeof (buf));
+	}
+    }
+
+  /* Check power mode.  */
+  if (power)
+    {
+      grub_printf ("Disk power mode is: ");
+      int mode = grub_hdparm_do_check_powermode_cmd (disk);
+      if (mode < 0)
+        grub_printf ("unknown\n");
+      else
+	grub_printf ("%s (0x%02x)\n",
+		     (mode == 0xff ? "active/idle" :
+		      mode == 0x80 ? "idle" :
+		      mode == 0x00 ? "standby" : "unknown"), mode);
+    }
+
+  /* Check health.  */
+  int status = 0;
+  if (health)
+    {
+      if (! quiet)
+	grub_printf ("SMART status is: ");
+      int err = grub_hdparm_do_smart_cmd (disk, GRUB_ATA_FEAT_SMART_STATUS);
+      if (! quiet)
+	grub_printf ("%s\n", (err  < 0 ? "unknown" :
+	                      err == 0 ? "OK" : "*BAD*"));
+      status = (err > 0);
+    }
+
+  /* Change power mode.  */
+  if (standby_now)
+    grub_hdparm_simple_cmd ("Set disk to standby mode", disk,
+			    GRUB_ATA_CMD_STANDBY_IMMEDIATE);
+
+  if (sleep_now)
+    grub_hdparm_simple_cmd ("Set disk to sleep mode", disk,
+			    GRUB_ATA_CMD_SLEEP);
+
+  grub_disk_close (disk);
+
+  grub_errno = GRUB_ERR_NONE;
+  return status;
+}
+
+
+GRUB_MOD_INIT(hdparm)
+{
+  (void) mod;
+
+  grub_register_command ("hdparm", grub_cmd_hdparm, GRUB_COMMAND_FLAG_BOTH,
+			 "hdparm [OPTIONS] DISK",
+			 "Get/set ATA disk parameters.", options);
+}
+
+GRUB_MOD_FINI(hdparm)
+{
+  grub_unregister_command ("hdparm");
+}

Modified: trunk/grub2/conf/i386-pc.mk
===================================================================
--- trunk/grub2/conf/i386-pc.mk	2009-02-13 20:26:56 UTC (rev 1991)
+++ trunk/grub2/conf/i386-pc.mk	2009-02-14 12:57:55 UTC (rev 1992)
@@ -979,7 +979,7 @@
 	vbe.mod vbetest.mod vbeinfo.mod play.mod serial.mod	\
 	ata.mod vga.mod memdisk.mod pci.mod lspci.mod \
 	aout.mod _bsd.mod bsd.mod pxe.mod pxecmd.mod datetime.mod date.mod \
-	datehook.mod lsmmap.mod \
+	datehook.mod lsmmap.mod ata_pthru.mod hdparm.mod \
 	usb.mod uhci.mod ohci.mod usbtest.mod usbms.mod
 
 # For biosdisk.mod.
@@ -3415,5 +3415,138 @@
 lsmmap_mod_CFLAGS = $(COMMON_CFLAGS)
 lsmmap_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+# For ata_pthru.mod.
+ata_pthru_mod_SOURCES = disk/ata_pthru.c
+CLEANFILES += ata_pthru.mod mod-ata_pthru.o mod-ata_pthru.c pre-ata_pthru.o ata_pthru_mod-disk_ata_pthru.o und-ata_pthru.lst
+ifneq ($(ata_pthru_mod_EXPORTS),no)
+CLEANFILES += def-ata_pthru.lst
+DEFSYMFILES += def-ata_pthru.lst
+endif
+MOSTLYCLEANFILES += ata_pthru_mod-disk_ata_pthru.d
+UNDSYMFILES += und-ata_pthru.lst
+
+ata_pthru.mod: pre-ata_pthru.o mod-ata_pthru.o $(TARGET_OBJ2ELF)
+	-rm -f $@
+	$(TARGET_CC) $(ata_pthru_mod_LDFLAGS) $(TARGET_LDFLAGS) $(MODULE_LDFLAGS) -Wl,-r,-d -o $@ pre-ata_pthru.o mod-ata_pthru.o
+	if test ! -z $(TARGET_OBJ2ELF); then ./$(TARGET_OBJ2ELF) $@ || (rm -f $@; exit 1); fi
+	$(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -K _grub_mod_init -K _grub_mod_fini -R .note -R .comment $@
+
+pre-ata_pthru.o: $(ata_pthru_mod_DEPENDENCIES) ata_pthru_mod-disk_ata_pthru.o
+	-rm -f $@
+	$(TARGET_CC) $(ata_pthru_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ ata_pthru_mod-disk_ata_pthru.o
+
+mod-ata_pthru.o: mod-ata_pthru.c
+	$(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(ata_pthru_mod_CFLAGS) -c -o $@ $<
+
+mod-ata_pthru.c: $(builddir)/moddep.lst $(srcdir)/genmodsrc.sh
+	sh $(srcdir)/genmodsrc.sh 'ata_pthru' $< > $@ || (rm -f $@; exit 1)
+
+ifneq ($(ata_pthru_mod_EXPORTS),no)
+def-ata_pthru.lst: pre-ata_pthru.o
+	$(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 ata_pthru/' > $@
+endif
+
+und-ata_pthru.lst: pre-ata_pthru.o
+	echo 'ata_pthru' > $@
+	$(NM) -u -P -p $< | cut -f1 -d' ' >> $@
+
+ata_pthru_mod-disk_ata_pthru.o: disk/ata_pthru.c $(disk/ata_pthru.c_DEPENDENCIES)
+	$(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS)  $(TARGET_CFLAGS) $(ata_pthru_mod_CFLAGS) -MD -c -o $@ $<
+-include ata_pthru_mod-disk_ata_pthru.d
+
+CLEANFILES += cmd-ata_pthru_mod-disk_ata_pthru.lst fs-ata_pthru_mod-disk_ata_pthru.lst partmap-ata_pthru_mod-disk_ata_pthru.lst
+COMMANDFILES += cmd-ata_pthru_mod-disk_ata_pthru.lst
+FSFILES += fs-ata_pthru_mod-disk_ata_pthru.lst
+PARTMAPFILES += partmap-ata_pthru_mod-disk_ata_pthru.lst
+
+cmd-ata_pthru_mod-disk_ata_pthru.lst: disk/ata_pthru.c $(disk/ata_pthru.c_DEPENDENCIES) gencmdlist.sh
+	set -e; 	  $(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(ata_pthru_mod_CFLAGS) -E $< 	  | sh $(srcdir)/gencmdlist.sh ata_pthru > $@ || (rm -f $@; exit 1)
+
+fs-ata_pthru_mod-disk_ata_pthru.lst: disk/ata_pthru.c $(disk/ata_pthru.c_DEPENDENCIES) genfslist.sh
+	set -e; 	  $(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(ata_pthru_mod_CFLAGS) -E $< 	  | sh $(srcdir)/genfslist.sh ata_pthru > $@ || (rm -f $@; exit 1)
+
+partmap-ata_pthru_mod-disk_ata_pthru.lst: disk/ata_pthru.c $(disk/ata_pthru.c_DEPENDENCIES) genpartmaplist.sh
+	set -e; 	  $(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(ata_pthru_mod_CFLAGS) -E $< 	  | sh $(srcdir)/genpartmaplist.sh ata_pthru > $@ || (rm -f $@; exit 1)
+
+
+ata_pthru_mod_CFLAGS = $(COMMON_CFLAGS)
+ata_pthru_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For hdparm.mod.
+hdparm_mod_SOURCES = commands/hdparm.c lib/hexdump.c
+CLEANFILES += hdparm.mod mod-hdparm.o mod-hdparm.c pre-hdparm.o hdparm_mod-commands_hdparm.o hdparm_mod-lib_hexdump.o und-hdparm.lst
+ifneq ($(hdparm_mod_EXPORTS),no)
+CLEANFILES += def-hdparm.lst
+DEFSYMFILES += def-hdparm.lst
+endif
+MOSTLYCLEANFILES += hdparm_mod-commands_hdparm.d hdparm_mod-lib_hexdump.d
+UNDSYMFILES += und-hdparm.lst
+
+hdparm.mod: pre-hdparm.o mod-hdparm.o $(TARGET_OBJ2ELF)
+	-rm -f $@
+	$(TARGET_CC) $(hdparm_mod_LDFLAGS) $(TARGET_LDFLAGS) $(MODULE_LDFLAGS) -Wl,-r,-d -o $@ pre-hdparm.o mod-hdparm.o
+	if test ! -z $(TARGET_OBJ2ELF); then ./$(TARGET_OBJ2ELF) $@ || (rm -f $@; exit 1); fi
+	$(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -K _grub_mod_init -K _grub_mod_fini -R .note -R .comment $@
+
+pre-hdparm.o: $(hdparm_mod_DEPENDENCIES) hdparm_mod-commands_hdparm.o hdparm_mod-lib_hexdump.o
+	-rm -f $@
+	$(TARGET_CC) $(hdparm_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ hdparm_mod-commands_hdparm.o hdparm_mod-lib_hexdump.o
+
+mod-hdparm.o: mod-hdparm.c
+	$(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(hdparm_mod_CFLAGS) -c -o $@ $<
+
+mod-hdparm.c: $(builddir)/moddep.lst $(srcdir)/genmodsrc.sh
+	sh $(srcdir)/genmodsrc.sh 'hdparm' $< > $@ || (rm -f $@; exit 1)
+
+ifneq ($(hdparm_mod_EXPORTS),no)
+def-hdparm.lst: pre-hdparm.o
+	$(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 hdparm/' > $@
+endif
+
+und-hdparm.lst: pre-hdparm.o
+	echo 'hdparm' > $@
+	$(NM) -u -P -p $< | cut -f1 -d' ' >> $@
+
+hdparm_mod-commands_hdparm.o: commands/hdparm.c $(commands/hdparm.c_DEPENDENCIES)
+	$(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS)  $(TARGET_CFLAGS) $(hdparm_mod_CFLAGS) -MD -c -o $@ $<
+-include hdparm_mod-commands_hdparm.d
+
+CLEANFILES += cmd-hdparm_mod-commands_hdparm.lst fs-hdparm_mod-commands_hdparm.lst partmap-hdparm_mod-commands_hdparm.lst
+COMMANDFILES += cmd-hdparm_mod-commands_hdparm.lst
+FSFILES += fs-hdparm_mod-commands_hdparm.lst
+PARTMAPFILES += partmap-hdparm_mod-commands_hdparm.lst
+
+cmd-hdparm_mod-commands_hdparm.lst: commands/hdparm.c $(commands/hdparm.c_DEPENDENCIES) gencmdlist.sh
+	set -e; 	  $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(hdparm_mod_CFLAGS) -E $< 	  | sh $(srcdir)/gencmdlist.sh hdparm > $@ || (rm -f $@; exit 1)
+
+fs-hdparm_mod-commands_hdparm.lst: commands/hdparm.c $(commands/hdparm.c_DEPENDENCIES) genfslist.sh
+	set -e; 	  $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(hdparm_mod_CFLAGS) -E $< 	  | sh $(srcdir)/genfslist.sh hdparm > $@ || (rm -f $@; exit 1)
+
+partmap-hdparm_mod-commands_hdparm.lst: commands/hdparm.c $(commands/hdparm.c_DEPENDENCIES) genpartmaplist.sh
+	set -e; 	  $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(hdparm_mod_CFLAGS) -E $< 	  | sh $(srcdir)/genpartmaplist.sh hdparm > $@ || (rm -f $@; exit 1)
+
+
+hdparm_mod-lib_hexdump.o: lib/hexdump.c $(lib/hexdump.c_DEPENDENCIES)
+	$(TARGET_CC) -Ilib -I$(srcdir)/lib $(TARGET_CPPFLAGS)  $(TARGET_CFLAGS) $(hdparm_mod_CFLAGS) -MD -c -o $@ $<
+-include hdparm_mod-lib_hexdump.d
+
+CLEANFILES += cmd-hdparm_mod-lib_hexdump.lst fs-hdparm_mod-lib_hexdump.lst partmap-hdparm_mod-lib_hexdump.lst
+COMMANDFILES += cmd-hdparm_mod-lib_hexdump.lst
+FSFILES += fs-hdparm_mod-lib_hexdump.lst
+PARTMAPFILES += partmap-hdparm_mod-lib_hexdump.lst
+
+cmd-hdparm_mod-lib_hexdump.lst: lib/hexdump.c $(lib/hexdump.c_DEPENDENCIES) gencmdlist.sh
+	set -e; 	  $(TARGET_CC) -Ilib -I$(srcdir)/lib $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(hdparm_mod_CFLAGS) -E $< 	  | sh $(srcdir)/gencmdlist.sh hdparm > $@ || (rm -f $@; exit 1)
+
+fs-hdparm_mod-lib_hexdump.lst: lib/hexdump.c $(lib/hexdump.c_DEPENDENCIES) genfslist.sh
+	set -e; 	  $(TARGET_CC) -Ilib -I$(srcdir)/lib $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(hdparm_mod_CFLAGS) -E $< 	  | sh $(srcdir)/genfslist.sh hdparm > $@ || (rm -f $@; exit 1)
+
+partmap-hdparm_mod-lib_hexdump.lst: lib/hexdump.c $(lib/hexdump.c_DEPENDENCIES) genpartmaplist.sh
+	set -e; 	  $(TARGET_CC) -Ilib -I$(srcdir)/lib $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(hdparm_mod_CFLAGS) -E $< 	  | sh $(srcdir)/genpartmaplist.sh hdparm > $@ || (rm -f $@; exit 1)
+
+
+hdparm_mod_CFLAGS = $(COMMON_CFLAGS)
+hdparm_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
 include $(srcdir)/conf/i386.mk
 include $(srcdir)/conf/common.mk

Modified: trunk/grub2/conf/i386-pc.rmk
===================================================================
--- trunk/grub2/conf/i386-pc.rmk	2009-02-13 20:26:56 UTC (rev 1991)
+++ trunk/grub2/conf/i386-pc.rmk	2009-02-14 12:57:55 UTC (rev 1992)
@@ -172,7 +172,7 @@
 	vbe.mod vbetest.mod vbeinfo.mod play.mod serial.mod	\
 	ata.mod vga.mod memdisk.mod pci.mod lspci.mod \
 	aout.mod _bsd.mod bsd.mod pxe.mod pxecmd.mod datetime.mod date.mod \
-	datehook.mod lsmmap.mod \
+	datehook.mod lsmmap.mod ata_pthru.mod hdparm.mod \
 	usb.mod uhci.mod ohci.mod usbtest.mod usbms.mod
 
 # For biosdisk.mod.
@@ -365,5 +365,15 @@
 lsmmap_mod_CFLAGS = $(COMMON_CFLAGS)
 lsmmap_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+# For ata_pthru.mod.
+ata_pthru_mod_SOURCES = disk/ata_pthru.c
+ata_pthru_mod_CFLAGS = $(COMMON_CFLAGS)
+ata_pthru_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For hdparm.mod.
+hdparm_mod_SOURCES = commands/hdparm.c lib/hexdump.c
+hdparm_mod_CFLAGS = $(COMMON_CFLAGS)
+hdparm_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
 include $(srcdir)/conf/i386.mk
 include $(srcdir)/conf/common.mk

Modified: trunk/grub2/disk/ata.c
===================================================================
--- trunk/grub2/disk/ata.c	2009-02-13 20:26:56 UTC (rev 1991)
+++ trunk/grub2/disk/ata.c	2009-02-14 12:57:55 UTC (rev 1992)
@@ -1,7 +1,7 @@
 /* ata.c - ATA disk access.  */
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2007, 2008  Free Software Foundation, Inc.
+ *  Copyright (C) 2007, 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
@@ -17,143 +17,22 @@
  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <grub/ata.h>
 #include <grub/dl.h>
-#include <grub/misc.h>
 #include <grub/disk.h>
 #include <grub/mm.h>
 #include <grub/time.h>
 #include <grub/pci.h>
 #include <grub/scsi.h>
-/* XXX: For now this only works on i386.  */
-#include <grub/cpu/io.h>
 
-typedef enum
-  {
-    GRUB_ATA_CHS,
-    GRUB_ATA_LBA,
-    GRUB_ATA_LBA48
-  } grub_ata_addressing_t;
-
 /* At the moment, only two IDE ports are supported.  */
 static const int grub_ata_ioaddress[] = { 0x1f0, 0x170 };
 static const int grub_ata_ioaddress2[] = { 0x3f6, 0x376 };
 
-#define GRUB_CDROM_SECTOR_SIZE	2048
-
-#define GRUB_ATA_REG_DATA	0
-#define GRUB_ATA_REG_ERROR	1
-#define GRUB_ATA_REG_FEATURES	1
-#define GRUB_ATA_REG_SECTORS	2
-#define GRUB_ATAPI_REG_IREASON	2
-#define GRUB_ATA_REG_SECTNUM	3
-#define GRUB_ATA_REG_CYLLSB	4
-#define GRUB_ATA_REG_CYLMSB	5
-#define GRUB_ATA_REG_LBALOW	3
-#define GRUB_ATA_REG_LBAMID	4
-#define GRUB_ATAPI_REG_CNTLOW	4
-#define GRUB_ATA_REG_LBAHIGH	5
-#define GRUB_ATAPI_REG_CNTHIGH	5
-#define GRUB_ATA_REG_DISK	6
-#define GRUB_ATA_REG_CMD	7
-#define GRUB_ATA_REG_STATUS	7
-
-#define GRUB_ATA_REG2_CONTROL	0
-
-#define GRUB_ATA_STATUS_ERR	0x01
-#define GRUB_ATA_STATUS_INDEX	0x02
-#define GRUB_ATA_STATUS_ECC	0x04
-#define GRUB_ATA_STATUS_DRQ	0x08
-#define GRUB_ATA_STATUS_SEEK	0x10
-#define GRUB_ATA_STATUS_WRERR	0x20
-#define GRUB_ATA_STATUS_READY	0x40
-#define GRUB_ATA_STATUS_BUSY	0x80
-
-/* ATAPI interrupt reason values (I/O, D/C bits).  */
-#define GRUB_ATAPI_IREASON_MASK     0x3
-#define GRUB_ATAPI_IREASON_DATA_OUT 0x0
-#define GRUB_ATAPI_IREASON_CMD_OUT  0x1
-#define GRUB_ATAPI_IREASON_DATA_IN  0x2
-#define GRUB_ATAPI_IREASON_ERROR    0x3
-
-enum grub_ata_commands
-  {
-    GRUB_ATA_CMD_READ_SECTORS = 0x20,
-    GRUB_ATA_CMD_READ_SECTORS_EXT = 0x24,
-    GRUB_ATA_CMD_WRITE_SECTORS = 0x30,
-    GRUB_ATA_CMD_WRITE_SECTORS_EXT = 0x34,
-    GRUB_ATA_CMD_IDENTIFY_DEVICE = 0xEC,
-    GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE = 0xA1,
-    GRUB_ATA_CMD_PACKET = 0xA0,
-  };
-
-enum grub_ata_timeout_milliseconds
-  {
-    GRUB_ATA_TOUT_STD  =  1000,  /* 1s standard timeout.  */
-    GRUB_ATA_TOUT_DATA = 10000   /* 10s DATA I/O timeout.  */
-  };
-
-struct grub_ata_device
-{
-  /* IDE port to use.  */
-  int port;
-
-  /* IO addresses on which the registers for this device can be
-     found.  */
-  int ioaddress;
-  int ioaddress2;
-
-  /* Two devices can be connected to a single cable.  Use this field
-     to select device 0 (commonly known as "master") or device 1
-     (commonly known as "slave").  */
-  int device;
-
-  /* Addressing methods available for accessing this device.  If CHS
-     is only available, use that.  Otherwise use LBA, except for the
-     high sectors.  In that case use LBA48.  */
-  grub_ata_addressing_t addr;
-
-  /* Sector count.  */
-  grub_uint64_t size;
-
-  /* CHS maximums.  */
-  grub_uint16_t cylinders;
-  grub_uint16_t heads;
-  grub_uint16_t sectors_per_track;
-
-  /* Set to 0 for ATA, set to 1 for ATAPI.  */
-  int atapi;
-
-  struct grub_ata_device *next;
-};
-
 static struct grub_ata_device *grub_ata_devices;
 
-static inline void
-grub_ata_regset (struct grub_ata_device *dev, int reg, int val)
-{
-  grub_outb (val, dev->ioaddress + reg);
-}
-
-static inline grub_uint8_t
-grub_ata_regget (struct grub_ata_device *dev, int reg)
-{
-  return grub_inb (dev->ioaddress + reg);
-}
-
-static inline void
-grub_ata_regset2 (struct grub_ata_device *dev, int reg, int val)
-{
-  grub_outb (val, dev->ioaddress2 + reg);
-}
-
-static inline grub_uint8_t
-grub_ata_regget2 (struct grub_ata_device *dev, int reg)
-{
-  return grub_inb (dev->ioaddress2 + reg);
-}
-
 /* Wait for !BSY.  */
-static grub_err_t
+grub_err_t
 grub_ata_wait_not_busy (struct grub_ata_device *dev, int milliseconds)
 {
   /* ATA requires 400ns (after a write to CMD register) or
@@ -183,18 +62,8 @@
   grub_millisleep (50);
 }
 
-/* Check for !BSY before issuing a new command.  */
-static inline grub_err_t
-grub_ata_check_ready (struct grub_ata_device *dev)
-{
-  if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY)
-    return grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_STD);
-
-  return GRUB_ERR_NONE;
-}
-
 /* Wait for !BSY, DRQ.  */
-static grub_err_t
+grub_err_t
 grub_ata_wait_drq (struct grub_ata_device *dev, int rw,
 		   int milliseconds)
 {
@@ -230,7 +99,7 @@
   dst[len] = '\0';
 }
 
-static void
+void
 grub_ata_pio_read (struct grub_ata_device *dev, char *buf, grub_size_t size)
 {
   grub_uint16_t *buf16 = (grub_uint16_t *) buf;

Added: trunk/grub2/disk/ata_pthru.c
===================================================================
--- trunk/grub2/disk/ata_pthru.c	                        (rev 0)
+++ trunk/grub2/disk/ata_pthru.c	2009-02-14 12:57:55 UTC (rev 1992)
@@ -0,0 +1,109 @@
+/* ata_pthru.c - ATA pass through for ata.mod.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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
+ *  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/ata.h>
+#include <grub/disk.h>
+#include <grub/dl.h>
+#include <grub/mm.h>
+
+
+/* ATA pass through support, used by hdparm.mod.  */
+static grub_err_t
+grub_ata_pass_through (grub_disk_t disk,
+		       struct grub_disk_ata_pass_through_parms *parms)
+{
+  if (disk->dev->id != GRUB_DISK_DEVICE_ATA_ID)
+    return grub_error (GRUB_ERR_BAD_DEVICE,
+		       "Device not accessed via ata.mod");
+
+  struct grub_ata_device *dev = (struct grub_ata_device *) disk->data;
+
+  if (! (parms->size == 0 || parms->size == GRUB_DISK_SECTOR_SIZE))
+    return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+		       "ATA multi-sector read and DATA OUT not implemented");
+
+  grub_dprintf ("ata", "ata_pass_through: cmd=0x%x, features=0x%x, sectors=0x%x\n",
+		parms->taskfile[GRUB_ATA_REG_CMD],
+		parms->taskfile[GRUB_ATA_REG_FEATURES],
+		parms->taskfile[GRUB_ATA_REG_SECTORS]);
+  grub_dprintf ("ata", "lba_high=0x%x, lba_mid=0x%x, lba_low=0x%x, size=%d\n",
+	        parms->taskfile[GRUB_ATA_REG_LBAHIGH],
+	        parms->taskfile[GRUB_ATA_REG_LBAMID],
+	        parms->taskfile[GRUB_ATA_REG_LBALOW], parms->size);
+
+  /* Set registers.  */
+  grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4
+		   | (parms->taskfile[GRUB_ATA_REG_DISK] & 0xf));
+  if (grub_ata_check_ready (dev))
+    return grub_errno;
+
+  int i;
+  for (i = GRUB_ATA_REG_FEATURES; i <= GRUB_ATA_REG_LBAHIGH; i++)
+    grub_ata_regset (dev, i, parms->taskfile[i]);
+
+  /* Start command. */
+  grub_ata_regset (dev, GRUB_ATA_REG_CMD, parms->taskfile[GRUB_ATA_REG_CMD]);
+
+  /* Wait for !BSY.  */
+  if (grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
+    return grub_errno;
+
+  /* Check status.  */
+  grub_int8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
+  grub_dprintf ("ata", "status=0x%x\n", sts);
+
+  /* Transfer data.  */
+  if ((sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) == GRUB_ATA_STATUS_DRQ)
+    {
+      if (parms->size != GRUB_DISK_SECTOR_SIZE)
+        return grub_error (GRUB_ERR_READ_ERROR, "DRQ unexpected");
+      grub_ata_pio_read (dev, parms->buffer, GRUB_DISK_SECTOR_SIZE);
+    }
+
+  /* Return registers.  */
+  for (i = GRUB_ATA_REG_ERROR; i <= GRUB_ATA_REG_STATUS; i++)
+    parms->taskfile[i] = grub_ata_regget (dev, i);
+
+  grub_dprintf ("ata", "status=0x%x, error=0x%x, sectors=0x%x\n",
+	        parms->taskfile[GRUB_ATA_REG_STATUS],
+	        parms->taskfile[GRUB_ATA_REG_ERROR],
+		parms->taskfile[GRUB_ATA_REG_SECTORS]);
+
+  if (parms->taskfile[GRUB_ATA_REG_STATUS]
+      & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
+    return grub_error (GRUB_ERR_READ_ERROR, "ATA passthrough failed");
+
+  return GRUB_ERR_NONE;
+}
+
+
+
+GRUB_MOD_INIT(ata_pthru)
+{
+  (void) mod;
+
+  /* Register ATA pass through function.  */
+  grub_disk_ata_pass_through = grub_ata_pass_through;
+}
+
+GRUB_MOD_FINI(ata_pthru)
+{
+  if (grub_disk_ata_pass_through == grub_ata_pass_through)
+    grub_disk_ata_pass_through = NULL;
+}

Added: trunk/grub2/include/grub/ata.h
===================================================================
--- trunk/grub2/include/grub/ata.h	                        (rev 0)
+++ trunk/grub2/include/grub/ata.h	2009-02-14 12:57:55 UTC (rev 1992)
@@ -0,0 +1,168 @@
+/* ata.h - ATA disk access.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2007, 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
+ *  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_ATA_HEADER
+#define GRUB_ATA_HEADER 1
+
+#include <grub/misc.h>
+#include <grub/symbol.h>
+/* XXX: For now this only works on i386.  */
+#include <grub/cpu/io.h>
+
+typedef enum
+  {
+    GRUB_ATA_CHS,
+    GRUB_ATA_LBA,
+    GRUB_ATA_LBA48
+  } grub_ata_addressing_t;
+
+#define GRUB_ATA_REG_DATA	0
+#define GRUB_ATA_REG_ERROR	1
+#define GRUB_ATA_REG_FEATURES	1
+#define GRUB_ATA_REG_SECTORS	2
+#define GRUB_ATAPI_REG_IREASON	2
+#define GRUB_ATA_REG_SECTNUM	3
+#define GRUB_ATA_REG_CYLLSB	4
+#define GRUB_ATA_REG_CYLMSB	5
+#define GRUB_ATA_REG_LBALOW	3
+#define GRUB_ATA_REG_LBAMID	4
+#define GRUB_ATAPI_REG_CNTLOW	4
+#define GRUB_ATA_REG_LBAHIGH	5
+#define GRUB_ATAPI_REG_CNTHIGH	5
+#define GRUB_ATA_REG_DISK	6
+#define GRUB_ATA_REG_CMD	7
+#define GRUB_ATA_REG_STATUS	7
+
+#define GRUB_ATA_REG2_CONTROL	0
+
+#define GRUB_ATA_STATUS_ERR	0x01
+#define GRUB_ATA_STATUS_INDEX	0x02
+#define GRUB_ATA_STATUS_ECC	0x04
+#define GRUB_ATA_STATUS_DRQ	0x08
+#define GRUB_ATA_STATUS_SEEK	0x10
+#define GRUB_ATA_STATUS_WRERR	0x20
+#define GRUB_ATA_STATUS_READY	0x40
+#define GRUB_ATA_STATUS_BUSY	0x80
+
+/* ATAPI interrupt reason values (I/O, D/C bits).  */
+#define GRUB_ATAPI_IREASON_MASK     0x3
+#define GRUB_ATAPI_IREASON_DATA_OUT 0x0
+#define GRUB_ATAPI_IREASON_CMD_OUT  0x1
+#define GRUB_ATAPI_IREASON_DATA_IN  0x2
+#define GRUB_ATAPI_IREASON_ERROR    0x3
+
+enum grub_ata_commands
+  {
+    GRUB_ATA_CMD_CHECK_POWER_MODE	= 0xe5,
+    GRUB_ATA_CMD_IDENTIFY_DEVICE	= 0xec,
+    GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE	= 0xa1,
+    GRUB_ATA_CMD_IDLE			= 0xe3,
+    GRUB_ATA_CMD_PACKET			= 0xa0,
+    GRUB_ATA_CMD_READ_SECTORS		= 0x20,
+    GRUB_ATA_CMD_READ_SECTORS_EXT	= 0x24,
+    GRUB_ATA_CMD_SECURITY_FREEZE_LOCK	= 0xf5,
+    GRUB_ATA_CMD_SET_FEATURES		= 0xef,
+    GRUB_ATA_CMD_SLEEP			= 0xe6,
+    GRUB_ATA_CMD_SMART			= 0xb0,
+    GRUB_ATA_CMD_STANDBY_IMMEDIATE	= 0xe0,
+    GRUB_ATA_CMD_WRITE_SECTORS		= 0x30,
+    GRUB_ATA_CMD_WRITE_SECTORS_EXT	= 0x34,
+  };
+
+enum grub_ata_timeout_milliseconds
+  {
+    GRUB_ATA_TOUT_STD  =  1000,  /* 1s standard timeout.  */
+    GRUB_ATA_TOUT_DATA = 10000   /* 10s DATA I/O timeout.  */
+  };
+
+struct grub_ata_device
+{
+  /* IDE port to use.  */
+  int port;
+
+  /* IO addresses on which the registers for this device can be
+     found.  */
+  int ioaddress;
+  int ioaddress2;
+
+  /* Two devices can be connected to a single cable.  Use this field
+     to select device 0 (commonly known as "master") or device 1
+     (commonly known as "slave").  */
+  int device;
+
+  /* Addressing methods available for accessing this device.  If CHS
+     is only available, use that.  Otherwise use LBA, except for the
+     high sectors.  In that case use LBA48.  */
+  grub_ata_addressing_t addr;
+
+  /* Sector count.  */
+  grub_uint64_t size;
+
+  /* CHS maximums.  */
+  grub_uint16_t cylinders;
+  grub_uint16_t heads;
+  grub_uint16_t sectors_per_track;
+
+  /* Set to 0 for ATA, set to 1 for ATAPI.  */
+  int atapi;
+
+  struct grub_ata_device *next;
+};
+
+grub_err_t EXPORT_FUNC(grub_ata_wait_not_busy) (struct grub_ata_device *dev,
+                                                int milliseconds);
+grub_err_t EXPORT_FUNC(grub_ata_wait_drq) (struct grub_ata_device *dev,
+					   int rw, int milliseconds);
+void EXPORT_FUNC(grub_ata_pio_read) (struct grub_ata_device *dev,
+				     char *buf, grub_size_t size);
+
+static inline void
+grub_ata_regset (struct grub_ata_device *dev, int reg, int val)
+{
+  grub_outb (val, dev->ioaddress + reg);
+}
+
+static inline grub_uint8_t
+grub_ata_regget (struct grub_ata_device *dev, int reg)
+{
+  return grub_inb (dev->ioaddress + reg);
+}
+
+static inline void
+grub_ata_regset2 (struct grub_ata_device *dev, int reg, int val)
+{
+  grub_outb (val, dev->ioaddress2 + reg);
+}
+
+static inline grub_uint8_t
+grub_ata_regget2 (struct grub_ata_device *dev, int reg)
+{
+  return grub_inb (dev->ioaddress2 + reg);
+}
+
+static inline grub_err_t
+grub_ata_check_ready (struct grub_ata_device *dev)
+{
+  if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY)
+    return grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_STD);
+
+  return GRUB_ERR_NONE;
+}
+
+#endif /* ! GRUB_ATA_HEADER */

Modified: trunk/grub2/include/grub/disk.h
===================================================================
--- trunk/grub2/include/grub/disk.h	2009-02-13 20:26:56 UTC (rev 1991)
+++ trunk/grub2/include/grub/disk.h	2009-02-14 12:57:55 UTC (rev 1992)
@@ -161,6 +161,17 @@
 extern void (* EXPORT_VAR(grub_disk_firmware_fini)) (void);
 extern int EXPORT_VAR(grub_disk_firmware_is_tainted);
 
+/* ATA pass through parameters and function.  */
+struct grub_disk_ata_pass_through_parms
+{
+  grub_uint8_t taskfile[8];
+  void * buffer;
+  int size;
+};
+
+extern grub_err_t (* EXPORT_VAR(grub_disk_ata_pass_through)) (grub_disk_t,
+		   struct grub_disk_ata_pass_through_parms *);
+
 #ifdef GRUB_UTIL
 void grub_raid_init (void);
 void grub_raid_fini (void);

Modified: trunk/grub2/kern/disk.c
===================================================================
--- trunk/grub2/kern/disk.c	2009-02-13 20:26:56 UTC (rev 1991)
+++ trunk/grub2/kern/disk.c	2009-02-14 12:57:55 UTC (rev 1992)
@@ -46,6 +46,10 @@
 void (*grub_disk_firmware_fini) (void);
 int grub_disk_firmware_is_tainted;
 
+grub_err_t (* grub_disk_ata_pass_through) (grub_disk_t,
+	    struct grub_ata_pass_through_cmd *);
+
+
 #if 0
 static unsigned long grub_disk_cache_hits;
 static unsigned long grub_disk_cache_misses;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.