From 9ff516f3ebf954b6711d8fa985d136f03027fe15 Mon Sep 17 00:00:00 2001 From: chrfranke Date: Sat, 14 Feb 2009 12:57:55 +0000 Subject: [PATCH] 2009-02-14 Christian Franke * 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 . Move and 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. --- ChangeLog | 38 ++++ DISTLIST | 3 + commands/hdparm.c | 421 ++++++++++++++++++++++++++++++++++++++++++++ conf/i386-pc.mk | 135 +++++++++++++- conf/i386-pc.rmk | 12 +- disk/ata.c | 141 +-------------- disk/ata_pthru.c | 109 ++++++++++++ include/grub/ata.h | 168 ++++++++++++++++++ include/grub/disk.h | 11 ++ kern/disk.c | 4 + 10 files changed, 904 insertions(+), 138 deletions(-) create mode 100644 commands/hdparm.c create mode 100644 disk/ata_pthru.c create mode 100644 include/grub/ata.h diff --git a/ChangeLog b/ChangeLog index a041adc49..3e09cb823 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,41 @@ +2009-02-14 Christian Franke + + * 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 . Move + and 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 Support multiple fallback entries, and provide an API to support diff --git a/DISTLIST b/DISTLIST index b5c8bfdea..63cff3dd6 100644 --- a/DISTLIST +++ b/DISTLIST @@ -49,6 +49,7 @@ commands/crc.c 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/sparc64-ieee1275.rmk 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/multiboot2.h 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 diff --git a/commands/hdparm.c b/commands/hdparm.c new file mode 100644 index 000000000..9f2ba905c --- /dev/null +++ b/commands/hdparm.c @@ -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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +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"); +} diff --git a/conf/i386-pc.mk b/conf/i386-pc.mk index 638334446..ee7dad8f1 100644 --- a/conf/i386-pc.mk +++ b/conf/i386-pc.mk @@ -979,7 +979,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. @@ -3415,5 +3415,138 @@ partmap-lsmmap_mod-commands_lsmmap.lst: commands/lsmmap.c $(commands/lsmmap.c_DE 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 diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index 3ef351e34..b8f1c6d8a 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -172,7 +172,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. @@ -365,5 +365,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 e981fe926..ed98b0b87 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 . */ +#include #include -#include #include #include #include #include #include -/* XXX: For now this only works on i386. */ -#include - -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 000000000..cc74eb41b --- /dev/null +++ b/disk/ata_pthru.c @@ -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 . + */ + +#include +#include +#include +#include + + +/* 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; +} diff --git a/include/grub/ata.h b/include/grub/ata.h new file mode 100644 index 000000000..aaa2e147a --- /dev/null +++ b/include/grub/ata.h @@ -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 . + */ + +#ifndef GRUB_ATA_HEADER +#define GRUB_ATA_HEADER 1 + +#include +#include +/* XXX: For now this only works on i386. */ +#include + +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 */ diff --git a/include/grub/disk.h b/include/grub/disk.h index f2fa421a5..1e8046a1d 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 ed82506fd..3f7e45110 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; -- 2.47.3