All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] hdparm.mod - get/set ATA disk parameters
@ 2009-01-24 22:59 Christian Franke
  2009-02-07 21:38 ` Robert Millan
  0 siblings, 1 reply; 14+ messages in thread
From: Christian Franke @ 2009-01-24 22:59 UTC (permalink / raw)
  To: grub-devel

[-- Attachment #1: Type: text/plain, Size: 1564 bytes --]

This patch adds a command which allows to change a few (S)ATA drive 
settings. It relies on a new pass-through function in ata.mod.

Command name and option syntax are borrowed from the well-known Linux tool.

Examples:

- Set AAM of system drive to "fast", other drive to "quiet":

hdparm -S 254 (ata0)
hdparm -S 128 (ata1)

- Change APM level
(reportedly useful to avoid clicking noise of some Notebook disks):

hdparm -B 254 (ata0)

- Set standby timeout to 10min
(e.g. if disk is likely not used in the selected boot configuration):

hdparm -S 120 (ata0)

- Set disk to standby or sleep mode (e.g. before running a long memtest):

hdparm -y (ata0)
hdparm -Y (ata0)

- Freeze security settings (some BIOS don't):

hdparm -F (ata0)

Thanks for any comment.

Christian


2009-01-24  Christian Franke  <franke@computer.org>

	* commands/hdparm.c: New file.  Provides `hdparm' command
	which sends ATA commands via grub_ata_pass_through.
	* conf/i386-pc.rmk: Add hdparm.mod.
	* disk/ata.c: 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.
	(grub_ata_pass_through): New function.
	(GRUB_MOD_INIT): Set grub_disk_ata_pass_through pointer.
	(GRUB_MOD_FINI): Reset grub_disk_ata_pass_through pointer.
	* include/grub/ata.h: New file, contains declarations from ata.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.



[-- Attachment #2: grub2-hdparm-mod.patch --]
[-- Type: text/x-diff, Size: 20211 bytes --]

diff --git a/commands/hdparm.c b/commands/hdparm.c
new file mode 100644
index 0000000..e829ced
--- /dev/null
+++ b/commands/hdparm.c
@@ -0,0 +1,340 @@
+/* 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},
+  {"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},
+  {"quiet",           'q', 0, "Do not print messages", 0, ARG_TYPE_NONE},
+  {0, 0, 0, 0, 0, 0}
+};
+
+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,
+			grub_uint8_t *ret_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;
+
+  if (ret_sectors)
+    *ret_sectors = apt.taskfile[GRUB_ATA_REG_SECTORS];
+  return GRUB_ERR_NONE;
+}
+
+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, 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, 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 and APM settings.  */
+  grub_uint16_t features = grub_le_to_cpu16 (idw[83]);
+  grub_uint16_t enabled  = grub_le_to_cpu16 (idw[86]);
+
+  grub_printf("Automatic Acoustic Management: ");
+  if (features & 0x0200)
+    {
+      if (enabled & 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 (features & 0x0008)
+    {
+      if (enabled & 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");
+
+  /* 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 ("no 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 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;
+  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 (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, NULL, 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 ");
+      grub_uint8_t mode = 0;
+      if (grub_hdparm_do_ata_cmd (disk, GRUB_ATA_CMD_CHECK_POWER_MODE,
+				  0, 0, &mode, NULL, 0))
+        grub_printf ("unknown\n");
+      else
+        grub_printf ("%s (0x%02x)\n",
+		     (mode == 0xff ? "active/idle" :
+		      mode == 0x80 ? "idle" :
+		      mode == 0x00 ? "standby" : "unknown"), mode);
+    }
+
+  /* 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 0;
+}
+
+
+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");
+}
diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk
index 0998252..a7d652c 100644
--- a/conf/i386-pc.rmk
+++ b/conf/i386-pc.rmk
@@ -164,7 +164,7 @@ pkglib_MODULES = biosdisk.mod _chain.mod _linux.mod linux.mod normal.mod \
 	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 hdparm.mod
 
 # For biosdisk.mod.
 biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
@@ -321,5 +321,10 @@ lsmmap_mod_SOURCES = commands/lsmmap.c
 lsmmap_mod_CFLAGS = $(COMMON_CFLAGS)
 lsmmap_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
diff --git a/disk/.ata.c.swp b/disk/.ata.c.swp
new file mode 100644
index 0000000..96cc004
Binary files /dev/null and b/disk/.ata.c.swp differ
diff --git a/disk/ata.c b/disk/ata.c
index e981fe9..6dd417f 100644
--- a/disk/ata.c
+++ b/disk/ata.c
@@ -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,6 +17,7 @@
  *  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>
@@ -38,54 +39,6 @@ typedef enum
 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.  */
@@ -962,6 +915,67 @@ static struct grub_scsi_dev grub_atapi_dev =
     .write = grub_atapi_write
   }; 
 
+/* 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, size=%d\n",
+	       parms->taskfile[GRUB_ATA_REG_CMD], parms->taskfile[GRUB_ATA_REG_FEATURES],
+	       parms->taskfile[GRUB_ATA_REG_SECTORS], 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=%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=%x, error=%x\n", sts,
+	       parms->taskfile[GRUB_ATA_REG_ERROR]);
+
+  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;
+}
+
 \f
 
 GRUB_MOD_INIT(ata)
@@ -983,10 +997,16 @@ GRUB_MOD_INIT(ata)
 
   /* ATAPI devices are handled by scsi.mod.  */
   grub_scsi_dev_register (&grub_atapi_dev);
+
+  /* Register ATA pass through function.  */
+  grub_disk_ata_pass_through = grub_ata_pass_through;
 }
 
 GRUB_MOD_FINI(ata)
 {
+  if (grub_disk_ata_pass_through == grub_ata_pass_through)
+    grub_disk_ata_pass_through = NULL;
+
   grub_scsi_dev_unregister (&grub_atapi_dev);
   grub_disk_dev_unregister (&grub_atadisk_dev);
 }
diff --git a/include/grub/ata.h b/include/grub/ata.h
new file mode 100644
index 0000000..f4c5d5b
--- /dev/null
+++ b/include/grub/ata.h
@@ -0,0 +1,76 @@
+/* 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_H
+#define GRUB_ATA_H
+
+#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_STANDBY_IMMEDIATE	= 0xe0,
+  GRUB_ATA_CMD_WRITE_SECTORS		= 0x30,
+  GRUB_ATA_CMD_WRITE_SECTORS_EXT	= 0x34,
+};
+
+#endif /* ! GRUB_ATA_H */
diff --git a/include/grub/disk.h b/include/grub/disk.h
index f2fa421..1e8046a 100644
--- a/include/grub/disk.h
+++ b/include/grub/disk.h
@@ -161,6 +161,17 @@ grub_uint64_t EXPORT_FUNC(grub_disk_get_size) (grub_disk_t disk);
 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);
diff --git a/kern/disk.c b/kern/disk.c
index ed82506..3f7e451 100644
--- a/kern/disk.c
+++ b/kern/disk.c
@@ -46,6 +46,10 @@ static struct grub_disk_cache grub_disk_cache_table[GRUB_DISK_CACHE_NUM];
 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;

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH] hdparm.mod - get/set ATA disk parameters
  2009-01-24 22:59 [PATCH] hdparm.mod - get/set ATA disk parameters Christian Franke
@ 2009-02-07 21:38 ` Robert Millan
  2009-02-07 22:46   ` Christian Franke
  0 siblings, 1 reply; 14+ messages in thread
From: Robert Millan @ 2009-02-07 21:38 UTC (permalink / raw)
  To: The development of GRUB 2

On Sat, Jan 24, 2009 at 11:59:01PM +0100, Christian Franke wrote:
> This patch adds a command which allows to change a few (S)ATA drive 
> settings. It relies on a new pass-through function in ata.mod.

Is this function going to be used for "normal" use of ata?  Note that ata.mod
should be as small as possible, since coreboot users usually want to include
it the GRUB that is installed to flash.

Would it make sense to put the function elsewhere?

-- 
Robert Millan

  The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
  how) you may access your data; but nobody's threatening your freedom: we
  still allow you to remove your data and not access it at all."



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] hdparm.mod - get/set ATA disk parameters
  2009-02-07 21:38 ` Robert Millan
@ 2009-02-07 22:46   ` Christian Franke
  2009-02-07 22:59     ` Robert Millan
  0 siblings, 1 reply; 14+ messages in thread
From: Christian Franke @ 2009-02-07 22:46 UTC (permalink / raw)
  To: The development of GRUB 2

Robert Millan wrote:
> On Sat, Jan 24, 2009 at 11:59:01PM +0100, Christian Franke wrote:
>   
>> This patch adds a command which allows to change a few (S)ATA drive 
>> settings. It relies on a new pass-through function in ata.mod.
>>     
>
> Is this function going to be used for "normal" use of ata?  Note that ata.mod
> should be as small as possible, since coreboot users usually want to include
> it the GRUB that is installed to flash.
>
>   

Like 'hdparm.mod' itself, the ATA pass-through function is not needed 
for normal boot.


> Would it make sense to put the function elsewhere?
>
>   

I would suggest to move grub_ata_pass_through() to a new module (e.g. 
ata2.mod, atax.mod, ataex.mod, ...)

The function should not be in hdparm.mod itself, because the hdparm 
command would also work with other ATA pass-through functions. For 
example with some future ahci.mod or even in conjunction with native USB 
support through a SAT tunnel.

Christian




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] hdparm.mod - get/set ATA disk parameters
  2009-02-07 22:46   ` Christian Franke
@ 2009-02-07 22:59     ` Robert Millan
  2009-02-07 23:12       ` Christian Franke
  0 siblings, 1 reply; 14+ messages in thread
From: Robert Millan @ 2009-02-07 22:59 UTC (permalink / raw)
  To: The development of GRUB 2

On Sat, Feb 07, 2009 at 11:46:39PM +0100, Christian Franke wrote:
> Robert Millan wrote:
> >On Sat, Jan 24, 2009 at 11:59:01PM +0100, Christian Franke wrote:
> >  
> >>This patch adds a command which allows to change a few (S)ATA drive 
> >>settings. It relies on a new pass-through function in ata.mod.
> >>    
> >
> >Is this function going to be used for "normal" use of ata?  Note that 
> >ata.mod
> >should be as small as possible, since coreboot users usually want to 
> >include
> >it the GRUB that is installed to flash.
> >
> >  
> 
> Like 'hdparm.mod' itself, the ATA pass-through function is not needed 
> for normal boot.

Well, I know it's not needed, but I was wondering if there's the possibility
that other functions in ata.mod could start using it later on, or so.

> I would suggest to move grub_ata_pass_through() to a new module (e.g. 
> ata2.mod, atax.mod, ataex.mod, ...)

How about ata_something.mod?  (consistent with ntfs.mod & ntfs_comp.mod)

-- 
Robert Millan

  The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
  how) you may access your data; but nobody's threatening your freedom: we
  still allow you to remove your data and not access it at all."



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] hdparm.mod - get/set ATA disk parameters
  2009-02-07 22:59     ` Robert Millan
@ 2009-02-07 23:12       ` Christian Franke
  2009-02-08  0:32         ` Robert Millan
  0 siblings, 1 reply; 14+ messages in thread
From: Christian Franke @ 2009-02-07 23:12 UTC (permalink / raw)
  To: The development of GRUB 2

Robert Millan wrote:
>
>> I would suggest to move grub_ata_pass_through() to a new module (e.g. 
>> ata2.mod, atax.mod, ataex.mod, ...)
>>     
>
> How about ata_something.mod?  (consistent with ntfs.mod & ntfs_comp.mod)
>
>   
ata_pthru.mod ?


Christian




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] hdparm.mod - get/set ATA disk parameters
  2009-02-07 23:12       ` Christian Franke
@ 2009-02-08  0:32         ` Robert Millan
  2009-02-09 19:42           ` Christian Franke
  2009-02-12 22:21           ` Christian Franke
  0 siblings, 2 replies; 14+ messages in thread
From: Robert Millan @ 2009-02-08  0:32 UTC (permalink / raw)
  To: The development of GRUB 2

On Sun, Feb 08, 2009 at 12:12:32AM +0100, Christian Franke wrote:
> Robert Millan wrote:
> >
> >>I would suggest to move grub_ata_pass_through() to a new module (e.g. 
> >>ata2.mod, atax.mod, ataex.mod, ...)
> >>    
> >
> >How about ata_something.mod?  (consistent with ntfs.mod & ntfs_comp.mod)
> >
> >  
> ata_pthru.mod ?

Deal!

-- 
Robert Millan

  The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
  how) you may access your data; but nobody's threatening your freedom: we
  still allow you to remove your data and not access it at all."



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] hdparm.mod - get/set ATA disk parameters
  2009-02-08  0:32         ` Robert Millan
@ 2009-02-09 19:42           ` Christian Franke
  2009-02-12 22:21           ` Christian Franke
  1 sibling, 0 replies; 14+ messages in thread
From: Christian Franke @ 2009-02-09 19:42 UTC (permalink / raw)
  To: The development of GRUB 2

Robert Millan wrote:
> On Sun, Feb 08, 2009 at 12:12:32AM +0100, Christian Franke wrote:
>   
>> Robert Millan wrote:
>>     
>>>> I would suggest to move grub_ata_pass_through() to a new module (e.g. 
>>>> ata2.mod, atax.mod, ataex.mod, ...)
>>>>    
>>>>         
>>> How about ata_something.mod?  (consistent with ntfs.mod & ntfs_comp.mod)
>>>
>>>       

It is actually ntfscomp.mod.


>>>  
>>>       
>> ata_pthru.mod ?
>>     
>
> Deal!
>
>   

I will prepare a patch - ata_pthru.mod, atapthru.mod or a more generic 
name to allow to add other ata_extension functions ?


Christian




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] hdparm.mod - get/set ATA disk parameters
  2009-02-08  0:32         ` Robert Millan
  2009-02-09 19:42           ` Christian Franke
@ 2009-02-12 22:21           ` Christian Franke
  2009-02-14 14:13             ` Christian Franke
  1 sibling, 1 reply; 14+ messages in thread
From: Christian Franke @ 2009-02-12 22:21 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 1827 bytes --]

Robert Millan wrote:
> On Sun, Feb 08, 2009 at 12:12:32AM +0100, Christian Franke wrote:
>   
>> Robert Millan wrote:
>>     
>>>> I would suggest to move grub_ata_pass_through() to a new module (e.g. 
>>>> ata2.mod, atax.mod, ataex.mod, ...)
>>>>    
>>>>         
>>> How about ata_something.mod?  (consistent with ntfs.mod & ntfs_comp.mod)
>>>
>>>  
>>>       
>> ata_pthru.mod ?
>>     
>
> Deal!
>
>   

New patch below. Function grub_ata_pass_through() now moved to new 
module ata_pthru.mod.

Christian


2009-02-12  Christian Franke  <franke@computer.org>

	* 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 ata.c.
	(enum grub_ata_commands): Add new commands for 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.



[-- Attachment #2: grub2-hdparm-mod-2.patch --]
[-- Type: text/x-patch, Size: 26296 bytes --]

diff --git a/commands/hdparm.c b/commands/hdparm.c
new file mode 100644
index 0000000..2e5a60f
--- /dev/null
+++ b/commands/hdparm.c
@@ -0,0 +1,340 @@
+/* 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},
+  {"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},
+  {"quiet",           'q', 0, "do not print messages", 0, ARG_TYPE_NONE},
+  {0, 0, 0, 0, 0, 0}
+};
+
+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,
+			grub_uint8_t *ret_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;
+
+  if (ret_sectors)
+    *ret_sectors = apt.taskfile[GRUB_ATA_REG_SECTORS];
+  return GRUB_ERR_NONE;
+}
+
+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, 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, 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 and APM settings.  */
+  grub_uint16_t features = grub_le_to_cpu16 (idw[83]);
+  grub_uint16_t enabled  = grub_le_to_cpu16 (idw[86]);
+
+  grub_printf("Automatic Acoustic Management: ");
+  if (features & 0x0200)
+    {
+      if (enabled & 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 (features & 0x0008)
+    {
+      if (enabled & 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");
+
+  /* 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 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;
+  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 (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, NULL, 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 ");
+      grub_uint8_t mode = 0;
+      if (grub_hdparm_do_ata_cmd (disk, GRUB_ATA_CMD_CHECK_POWER_MODE,
+				  0, 0, &mode, NULL, 0))
+        grub_printf ("unknown\n");
+      else
+        grub_printf ("%s (0x%02x)\n",
+		     (mode == 0xff ? "active/idle" :
+		      mode == 0x80 ? "idle" :
+		      mode == 0x00 ? "standby" : "unknown"), mode);
+    }
+
+  /* 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 0;
+}
+
+
+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");
+}
diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk
index 2fd03b5..6fd565c 100644
--- a/conf/i386-pc.rmk
+++ b/conf/i386-pc.rmk
@@ -171,7 +171,7 @@ pkglib_MODULES = biosdisk.mod _chain.mod _linux.mod linux.mod normal.mod \
 	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.
@@ -363,5 +363,15 @@ lsmmap_mod_SOURCES = commands/lsmmap.c
 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
diff --git a/disk/ata.c b/disk/ata.c
index e981fe9..ed98b0b 100644
--- a/disk/ata.c
+++ b/disk/ata.c
@@ -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_ata_wait (void)
   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 @@ grub_ata_strncpy (char *dst, char *src, grub_size_t len)
   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;
diff --git a/disk/ata_pthru.c b/disk/ata_pthru.c
new file mode 100644
index 0000000..d5d1f59
--- /dev/null
+++ b/disk/ata_pthru.c
@@ -0,0 +1,103 @@
+/* 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, size=%d\n",
+	       parms->taskfile[GRUB_ATA_REG_CMD], parms->taskfile[GRUB_ATA_REG_FEATURES],
+	       parms->taskfile[GRUB_ATA_REG_SECTORS], 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=%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=%x, error=%x\n",
+	       parms->taskfile[GRUB_ATA_REG_STATUS],
+	       parms->taskfile[GRUB_ATA_REG_ERROR]);
+
+  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;
+}
+
+\f
+
+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;
+}
diff --git a/include/grub/ata.h b/include/grub/ata.h
new file mode 100644
index 0000000..6ce6309
--- /dev/null
+++ b/include/grub/ata.h
@@ -0,0 +1,167 @@
+/* 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_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 */
diff --git a/include/grub/disk.h b/include/grub/disk.h
index f2fa421..1e8046a 100644
--- a/include/grub/disk.h
+++ b/include/grub/disk.h
@@ -161,6 +161,17 @@ grub_uint64_t EXPORT_FUNC(grub_disk_get_size) (grub_disk_t disk);
 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);
diff --git a/kern/disk.c b/kern/disk.c
index ed82506..3f7e451 100644
--- a/kern/disk.c
+++ b/kern/disk.c
@@ -46,6 +46,10 @@ static struct grub_disk_cache grub_disk_cache_table[GRUB_DISK_CACHE_NUM];
 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;

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH] hdparm.mod - get/set ATA disk parameters
  2009-02-12 22:21           ` Christian Franke
@ 2009-02-14 14:13             ` Christian Franke
  2009-02-21 13:05               ` Robert Millan
  0 siblings, 1 reply; 14+ messages in thread
From: Christian Franke @ 2009-02-14 14:13 UTC (permalink / raw)
  To: The development of GRUB 2

Christian Franke wrote:
> ...
> New patch below. Function grub_ata_pass_through() now moved to new 
> module ata_pthru.mod.
>

Committed. Now also includes a SMART status check.


grub.cfg example (assumes ata.mod is used):

...
insmod ata_pthru
insmod hdparm

# Make sure disks cannot be locked by an ATA password
hdparm --quiet --security-freeze (ata4)
hdparm --quiet --security-freeze (ata6)

menuentry "Boot" {

  # Check health
  if hdparm --quiet --health (ata4) ; then echo -n ; else
    echo "Warning: SMART status check failed"
    read
  fi

  # Set boot disk to "fast", disable spin down
  hdparm --quiet --aam=254 --standby-timeout=0 (ata4)

  # Set other disk to "quiet", spin down after 5min inactivity
  hdparm --quiet --aam=128 --standby-timeout=60 (ata6)

  # Boot ...
}

menuentry "Memory Test" {

  # Spin down both disks after 10min
  hdparm --quiet --standby-timeout=120 (ata4)
  hdparm --quiet --standby-timeout=120 (ata6)

  # Load memtest ...
}


Christian




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] hdparm.mod - get/set ATA disk parameters
  2009-02-14 14:13             ` Christian Franke
@ 2009-02-21 13:05               ` Robert Millan
  2009-02-21 18:00                 ` Christian Franke
  0 siblings, 1 reply; 14+ messages in thread
From: Robert Millan @ 2009-02-21 13:05 UTC (permalink / raw)
  To: The development of GRUB 2

On Sat, Feb 14, 2009 at 03:13:31PM +0100, Christian Franke wrote:
> insmod ata_pthru

(note that module dependencies should make this unnecessary)

> insmod hdparm
>
> # Make sure disks cannot be locked by an ATA password
> hdparm --quiet --security-freeze (ata4)
> hdparm --quiet --security-freeze (ata6)
>
> menuentry "Boot" {
>
>  # Check health
>  if hdparm --quiet --health (ata4) ; then echo -n ; else
>    echo "Warning: SMART status check failed"
>    read
>  fi
>
>  # Set boot disk to "fast", disable spin down
>  hdparm --quiet --aam=254 --standby-timeout=0 (ata4)
>
>  # Set other disk to "quiet", spin down after 5min inactivity
>  hdparm --quiet --aam=128 --standby-timeout=60 (ata6)
>
>  # Boot ...
> }
>
> menuentry "Memory Test" {
>
>  # Spin down both disks after 10min
>  hdparm --quiet --standby-timeout=120 (ata4)
>  hdparm --quiet --standby-timeout=120 (ata6)
>
>  # Load memtest ...
> }

Very interesting.  Do you think any of these features could be useful as a
default option in grub-mkconfig?

-- 
Robert Millan

  The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
  how) you may access your data; but nobody's threatening your freedom: we
  still allow you to remove your data and not access it at all."



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] hdparm.mod - get/set ATA disk parameters
  2009-02-21 13:05               ` Robert Millan
@ 2009-02-21 18:00                 ` Christian Franke
  2009-02-21 19:51                   ` Robert Millan
  0 siblings, 1 reply; 14+ messages in thread
From: Christian Franke @ 2009-02-21 18:00 UTC (permalink / raw)
  To: The development of GRUB 2

Robert Millan wrote:
> On Sat, Feb 14, 2009 at 03:13:31PM +0100, Christian Franke wrote:
>   
>> insmod ata_pthru
>>     
>
> (note that module dependencies should make this unnecessary)
>
>   

This insmod is necessary for now. hdparm.mod does not directly call 
ata_pthru.mod, it uses kern/disk.c::grub_disk_ata_pass_through function 
pointer instead. Otherwise, using other future ata_pass_through 
functions (e.g. from some ahci.mod :-) would not be possible.

Another drawback of a hdparm->ata_pthru dependency would be that 'help' 
or 'hdparm -h' would load ata.mod which disables e.g. biosdisk.mod.


>> insmod hdparm
>>
>> # Make sure disks cannot be locked by an ATA password
>> hdparm --quiet --security-freeze (ata4)
>> hdparm --quiet --security-freeze (ata6)
>>
>> menuentry "Boot" {
>>
>>  # Check health
>>  if hdparm --quiet --health (ata4) ; then echo -n ; else
>>    echo "Warning: SMART status check failed"
>>    read
>>  fi
>>
>>  # Set boot disk to "fast", disable spin down
>>  hdparm --quiet --aam=254 --standby-timeout=0 (ata4)
>>
>>  # Set other disk to "quiet", spin down after 5min inactivity
>>  hdparm --quiet --aam=128 --standby-timeout=60 (ata6)
>>
>>  # Boot ...
>> }
>>
>> menuentry "Memory Test" {
>>
>>  # Spin down both disks after 10min
>>  hdparm --quiet --standby-timeout=120 (ata4)
>>  hdparm --quiet --standby-timeout=120 (ata6)
>>
>>  # Load memtest ...
>> }
>>     
>
> Very interesting.  Do you think any of these features could be useful as a
> default option in grub-mkconfig?
>
>   

At least the --health check and --security-freeze are IMO recommended 
for each disk. Both is also done by a typical PC BIOS. AAM and standby 
settings are more user specific. The problem is that this relies on 
native ATA support which is now only available for controllers supported 
by ata.mod.

At least for the boot disk, such hdparm calls could be added by e.g. 
extending 'prepare_grub_to_access_device'.

Christian




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] hdparm.mod - get/set ATA disk parameters
  2009-02-21 18:00                 ` Christian Franke
@ 2009-02-21 19:51                   ` Robert Millan
  2009-02-21 20:38                     ` Christian Franke
  0 siblings, 1 reply; 14+ messages in thread
From: Robert Millan @ 2009-02-21 19:51 UTC (permalink / raw)
  To: The development of GRUB 2

On Sat, Feb 21, 2009 at 07:00:06PM +0100, Christian Franke wrote:
>>
>> Very interesting.  Do you think any of these features could be useful as a
>> default option in grub-mkconfig?
>>
>>   
>
> At least the --health check and --security-freeze are IMO recommended  
> for each disk. Both is also done by a typical PC BIOS. AAM and standby  
> settings are more user specific. The problem is that this relies on  
> native ATA support which is now only available for controllers supported  
> by ata.mod.
>
> At least for the boot disk, such hdparm calls could be added by e.g.  
> extending 'prepare_grub_to_access_device'.

But if the firmware already did, wouldn't this be a waste of boot time?

-- 
Robert Millan

  The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
  how) you may access your data; but nobody's threatening your freedom: we
  still allow you to remove your data and not access it at all."



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] hdparm.mod - get/set ATA disk parameters
  2009-02-21 19:51                   ` Robert Millan
@ 2009-02-21 20:38                     ` Christian Franke
  2009-02-21 20:46                       ` Robert Millan
  0 siblings, 1 reply; 14+ messages in thread
From: Christian Franke @ 2009-02-21 20:38 UTC (permalink / raw)
  To: The development of GRUB 2

Robert Millan wrote:
> On Sat, Feb 21, 2009 at 07:00:06PM +0100, Christian Franke wrote:
>   
>>> Very interesting.  Do you think any of these features could be useful as a
>>> default option in grub-mkconfig?
>>>
>>>   
>>>       
>> At least the --health check and --security-freeze are IMO recommended  
>> for each disk. Both is also done by a typical PC BIOS. AAM and standby  
>> settings are more user specific. The problem is that this relies on  
>> native ATA support which is now only available for controllers supported  
>> by ata.mod.
>>
>> At least for the boot disk, such hdparm calls could be added by e.g.  
>> extending 'prepare_grub_to_access_device'.
>>     
>
> But if the firmware already did, wouldn't this be a waste of boot time?
>
>   

Yes, it would. Most BIOS perform both health check and security freeze, 
but some don't. For coreboot, it is not a waste of boot time.

So if this is added to grub-mkconfig, it should be configurable by some 
variable.

-- 
Christian Franke




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] hdparm.mod - get/set ATA disk parameters
  2009-02-21 20:38                     ` Christian Franke
@ 2009-02-21 20:46                       ` Robert Millan
  0 siblings, 0 replies; 14+ messages in thread
From: Robert Millan @ 2009-02-21 20:46 UTC (permalink / raw)
  To: The development of GRUB 2

On Sat, Feb 21, 2009 at 09:38:39PM +0100, Christian Franke wrote:
>>
>> But if the firmware already did, wouldn't this be a waste of boot time?
>>
>>   
>
> Yes, it would. Most BIOS perform both health check and security freeze,  
> but some don't. For coreboot, it is not a waste of boot time.
>
> So if this is added to grub-mkconfig, it should be configurable by some  
> variable.

When using coreboot with GRUB, you usually have either:

  - A single GRUB, in chip, with a hand-crafted configuration.

  - Two GRUBs, one in chip which loads the other one from disk.  The
    configuration for the first is hand-crafted but the second one would
    usually be grub-mkconfig'ed.

In this case sounds like the right place for this would be the hand-crafted
part.  There's some example script code in:

  http://grub.enbug.org/CoreBoot

How about adding it there?

-- 
Robert Millan

  The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
  how) you may access your data; but nobody's threatening your freedom: we
  still allow you to remove your data and not access it at all."



^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2009-02-21 20:47 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-01-24 22:59 [PATCH] hdparm.mod - get/set ATA disk parameters Christian Franke
2009-02-07 21:38 ` Robert Millan
2009-02-07 22:46   ` Christian Franke
2009-02-07 22:59     ` Robert Millan
2009-02-07 23:12       ` Christian Franke
2009-02-08  0:32         ` Robert Millan
2009-02-09 19:42           ` Christian Franke
2009-02-12 22:21           ` Christian Franke
2009-02-14 14:13             ` Christian Franke
2009-02-21 13:05               ` Robert Millan
2009-02-21 18:00                 ` Christian Franke
2009-02-21 19:51                   ` Robert Millan
2009-02-21 20:38                     ` Christian Franke
2009-02-21 20:46                       ` Robert Millan

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.