2009-10-28 Robert Millan <rmh.grub@aybabtu.com>
[grub2/jjazz.git] / commands / hdparm.c
blob389954c45126ea0d0c2712fd329b79b6f420d089
1 /* hdparm.c - command to get/set ATA disk parameters. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2009 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/ata.h>
21 #include <grub/disk.h>
22 #include <grub/dl.h>
23 #include <grub/misc.h>
24 #include <grub/mm.h>
25 #include <grub/lib/hexdump.h>
26 #include <grub/extcmd.h>
28 static const struct grub_arg_option options[] = {
29 {"apm", 'B', 0, "set Advanced Power Management\n"
30 "(1=low, ..., 254=high, 255=off)",
31 0, ARG_TYPE_INT},
32 {"power", 'C', 0, "check power mode", 0, ARG_TYPE_NONE},
33 {"security-freeze", 'F', 0, "freeze ATA security settings until reset",
34 0, ARG_TYPE_NONE},
35 {"health", 'H', 0, "check SMART health status", 0, ARG_TYPE_NONE},
36 {"aam", 'M', 0, "set Automatic Acoustic Management\n"
37 "(0=off, 128=quiet, ..., 254=fast)",
38 0, ARG_TYPE_INT},
39 {"standby-timeout", 'S', 0, "set standby timeout\n"
40 "(0=off, 1=5s, 2=10s, ..., 240=20m, 241=30m, ...)",
41 0, ARG_TYPE_INT},
42 {"standby", 'y', 0, "set drive to standby mode", 0, ARG_TYPE_NONE},
43 {"sleep", 'Y', 0, "set drive to sleep mode", 0, ARG_TYPE_NONE},
44 {"identify", 'i', 0, "print drive identity and settings",
45 0, ARG_TYPE_NONE},
46 {"dumpid", 'I', 0, "dump contents of ATA IDENTIFY sector",
47 0, ARG_TYPE_NONE},
48 {"smart", -1, 0, "disable/enable SMART (0/1)", 0, ARG_TYPE_INT},
49 {"quiet", 'q', 0, "do not print messages", 0, ARG_TYPE_NONE},
50 {0, 0, 0, 0, 0, 0}
53 enum grub_ata_smart_commands
55 GRUB_ATA_FEAT_SMART_ENABLE = 0xd8,
56 GRUB_ATA_FEAT_SMART_DISABLE = 0xd9,
57 GRUB_ATA_FEAT_SMART_STATUS = 0xda,
60 static int quiet = 0;
62 static grub_err_t
63 grub_hdparm_do_ata_cmd (grub_disk_t disk, grub_uint8_t cmd,
64 grub_uint8_t features, grub_uint8_t sectors,
65 void * buffer, int size)
67 struct grub_disk_ata_pass_through_parms apt;
68 grub_memset (&apt, 0, sizeof (apt));
70 apt.taskfile[GRUB_ATA_REG_CMD] = cmd;
71 apt.taskfile[GRUB_ATA_REG_FEATURES] = features;
72 apt.taskfile[GRUB_ATA_REG_SECTORS] = sectors;
73 apt.buffer = buffer;
74 apt.size = size;
76 if (grub_disk_ata_pass_through (disk, &apt))
77 return grub_errno;
79 return GRUB_ERR_NONE;
82 static int
83 grub_hdparm_do_check_powermode_cmd (grub_disk_t disk)
85 struct grub_disk_ata_pass_through_parms apt;
86 grub_memset (&apt, 0, sizeof (apt));
88 apt.taskfile[GRUB_ATA_REG_CMD] = GRUB_ATA_CMD_CHECK_POWER_MODE;
90 if (grub_disk_ata_pass_through (disk, &apt))
91 return -1;
93 return apt.taskfile[GRUB_ATA_REG_SECTORS];
96 static int
97 grub_hdparm_do_smart_cmd (grub_disk_t disk, grub_uint8_t features)
99 struct grub_disk_ata_pass_through_parms apt;
100 grub_memset (&apt, 0, sizeof (apt));
102 apt.taskfile[GRUB_ATA_REG_CMD] = GRUB_ATA_CMD_SMART;
103 apt.taskfile[GRUB_ATA_REG_FEATURES] = features;
104 apt.taskfile[GRUB_ATA_REG_LBAMID] = 0x4f;
105 apt.taskfile[GRUB_ATA_REG_LBAHIGH] = 0xc2;
107 if (grub_disk_ata_pass_through (disk, &apt))
108 return -1;
110 if (features == GRUB_ATA_FEAT_SMART_STATUS)
112 if ( apt.taskfile[GRUB_ATA_REG_LBAMID] == 0x4f
113 && apt.taskfile[GRUB_ATA_REG_LBAHIGH] == 0xc2)
114 return 0; /* Good SMART status. */
115 else if ( apt.taskfile[GRUB_ATA_REG_LBAMID] == 0xf4
116 && apt.taskfile[GRUB_ATA_REG_LBAHIGH] == 0x2c)
117 return 1; /* Bad SMART status. */
118 else
119 return -1;
121 return 0;
124 static grub_err_t
125 grub_hdparm_simple_cmd (const char * msg,
126 grub_disk_t disk, grub_uint8_t cmd)
128 if (! quiet && msg)
129 grub_printf ("%s", msg);
131 grub_err_t err = grub_hdparm_do_ata_cmd (disk, cmd, 0, 0, NULL, 0);
133 if (! quiet && msg)
134 grub_printf ("%s\n", ! err ? "" : ": not supported");
135 return err;
138 static grub_err_t
139 grub_hdparm_set_val_cmd (const char * msg, int val,
140 grub_disk_t disk, grub_uint8_t cmd,
141 grub_uint8_t features, grub_uint8_t sectors)
143 if (! quiet && msg && *msg)
145 if (val >= 0)
146 grub_printf ("Set %s to %d", msg, val);
147 else
148 grub_printf ("Disable %s", msg);
151 grub_err_t err = grub_hdparm_do_ata_cmd (disk, cmd, features, sectors,
152 NULL, 0);
154 if (! quiet && msg)
155 grub_printf ("%s\n", ! err ? "" : ": not supported");
156 return err;
159 static const char *
160 le16_to_char (char *dest, const grub_uint16_t * src16, unsigned bytes)
162 grub_uint16_t * dest16 = (grub_uint16_t *) dest;
163 unsigned i;
164 for (i = 0; i < bytes / 2; i++)
165 dest16[i] = grub_be_to_cpu16 (src16[i]);
166 return dest;
169 static void
170 grub_hdparm_print_identify (const char * idbuf)
172 const grub_uint16_t * idw = (const grub_uint16_t *) idbuf;
174 /* Print identity strings. */
175 char tmp[40];
176 grub_printf ("Model: \"%.40s\"\n", le16_to_char (tmp, &idw[27], 40));
177 grub_printf ("Firmware: \"%.8s\"\n", le16_to_char (tmp, &idw[23], 8));
178 grub_printf ("Serial: \"%.20s\"\n", le16_to_char (tmp, &idw[10], 20));
180 /* Print AAM, APM and SMART settings. */
181 grub_uint16_t features1 = grub_le_to_cpu16 (idw[82]);
182 grub_uint16_t features2 = grub_le_to_cpu16 (idw[83]);
183 grub_uint16_t enabled1 = grub_le_to_cpu16 (idw[85]);
184 grub_uint16_t enabled2 = grub_le_to_cpu16 (idw[86]);
186 grub_printf ("Automatic Acoustic Management: ");
187 if (features2 & 0x0200)
189 if (enabled2 & 0x0200)
191 grub_uint16_t aam = grub_le_to_cpu16 (idw[94]);
192 grub_printf ("%u (128=quiet, ..., 254=fast, recommended=%u)\n",
193 aam & 0xff, (aam >> 8) & 0xff);
195 else
196 grub_printf ("disabled\n");
198 else
199 grub_printf ("not supported\n");
201 grub_printf ("Advanced Power Management: ");
202 if (features2 & 0x0008)
204 if (enabled2 & 0x0008)
205 grub_printf ("%u (1=low, ..., 254=high)\n",
206 grub_le_to_cpu16 (idw[91]) & 0xff);
207 else
208 grub_printf ("disabled\n");
210 else
211 grub_printf ("not supported\n");
213 grub_printf ("SMART Feature Set: ");
214 if (features1 & 0x0001)
215 grub_printf ("%sabled\n", (enabled1 & 0x0001 ? "en" : "dis"));
216 else
217 grub_printf ("not supported\n");
219 /* Print security settings. */
220 grub_uint16_t security = grub_le_to_cpu16 (idw[128]);
222 grub_printf ("ATA Security: ");
223 if (security & 0x0001)
224 grub_printf ("%s, %s, %s, %s\n",
225 (security & 0x0002 ? "ENABLED" : "disabled"),
226 (security & 0x0004 ? "**LOCKED**" : "not locked"),
227 (security & 0x0008 ? "frozen" : "NOT FROZEN"),
228 (security & 0x0010 ? "COUNT EXPIRED" : "count not expired"));
229 else
230 grub_printf ("not supported\n");
233 static void
234 grub_hdparm_print_standby_tout (int timeout)
236 if (timeout == 0)
237 grub_printf ("off");
238 else if (timeout <= 252 || timeout == 255)
240 int h = 0, m = 0 , s = 0;
241 if (timeout == 255)
243 m = 21;
244 s = 15;
246 else if (timeout == 252)
247 m = 21;
248 else if (timeout <= 240)
250 s = timeout * 5;
251 m = s / 60;
252 s %= 60;
254 else
256 m = (timeout - 240) * 30;
257 h = m / 60;
258 m %= 60;
260 grub_printf ("%02d:%02d:%02d", h, m, s);
262 else
263 grub_printf ("invalid or vendor-specific");
266 static int get_int_arg (const struct grub_arg_list *state)
268 return (state->set ? (int)grub_strtoul (state->arg, 0, 0) : -1);
271 static grub_err_t
272 grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
274 struct grub_arg_list *state = cmd->state;
276 /* Check command line. */
277 if (argc != 1)
278 return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing device name argument");
280 grub_size_t len = grub_strlen (args[0]);
281 if (! (args[0][0] == '(' && args[0][len - 1] == ')'))
282 return grub_error (GRUB_ERR_BAD_ARGUMENT, "argument is not a device name");
283 args[0][len - 1] = 0;
285 if (! grub_disk_ata_pass_through)
286 return grub_error (GRUB_ERR_BAD_ARGUMENT, "ATA pass through not available");
288 int i = 0;
289 int apm = get_int_arg (&state[i++]);
290 int power = state[i++].set;
291 int sec_freeze = state[i++].set;
292 int health = state[i++].set;
293 int aam = get_int_arg (&state[i++]);
294 int standby_tout = get_int_arg (&state[i++]);
295 int standby_now = state[i++].set;
296 int sleep_now = state[i++].set;
297 int ident = state[i++].set;
298 int dumpid = state[i++].set;
299 int enable_smart = get_int_arg (&state[i++]);
300 quiet = state[i++].set;
302 /* Open disk. */
303 grub_disk_t disk = grub_disk_open (&args[0][1]);
304 if (! disk)
305 return grub_errno;
307 if (disk->partition)
309 grub_disk_close (disk);
310 return grub_error (GRUB_ERR_BAD_ARGUMENT, "partition not allowed");
313 /* Change settings. */
314 if (aam >= 0)
315 grub_hdparm_set_val_cmd ("Automatic Acoustic Management", (aam ? aam : -1),
316 disk, GRUB_ATA_CMD_SET_FEATURES, (aam ? 0x42 : 0xc2), aam);
318 if (apm >= 0)
319 grub_hdparm_set_val_cmd ("Advanced Power Management",
320 (apm != 255 ? apm : -1), disk, GRUB_ATA_CMD_SET_FEATURES,
321 (apm != 255 ? 0x05 : 0x85), (apm != 255 ? apm : 0));
323 if (standby_tout >= 0)
325 if (! quiet)
327 grub_printf ("Set standby timeout to %d (", standby_tout);
328 grub_hdparm_print_standby_tout (standby_tout);
329 grub_printf (")");
331 /* The IDLE cmd sets disk to idle mode and configures standby timer. */
332 grub_hdparm_set_val_cmd ("", -1, disk, GRUB_ATA_CMD_IDLE, 0, standby_tout);
335 if (enable_smart >= 0)
337 if (! quiet)
338 grub_printf ("%sable SMART operations", (enable_smart ? "En" : "Dis"));
339 int err = grub_hdparm_do_smart_cmd (disk, (enable_smart ?
340 GRUB_ATA_FEAT_SMART_ENABLE : GRUB_ATA_FEAT_SMART_DISABLE));
341 if (! quiet)
342 grub_printf ("%s\n", err ? ": not supported" : "");
345 if (sec_freeze)
346 grub_hdparm_simple_cmd ("Freeze security settings", disk,
347 GRUB_ATA_CMD_SECURITY_FREEZE_LOCK);
349 /* Print/dump IDENTIFY. */
350 if (ident || dumpid)
352 char buf[GRUB_DISK_SECTOR_SIZE];
353 if (grub_hdparm_do_ata_cmd (disk, GRUB_ATA_CMD_IDENTIFY_DEVICE,
354 0, 0, buf, sizeof (buf)))
355 grub_printf ("Cannot read ATA IDENTIFY data\n");
356 else
358 if (ident)
359 grub_hdparm_print_identify (buf);
360 if (dumpid)
361 hexdump (0, buf, sizeof (buf));
365 /* Check power mode. */
366 if (power)
368 grub_printf ("Disk power mode is: ");
369 int mode = grub_hdparm_do_check_powermode_cmd (disk);
370 if (mode < 0)
371 grub_printf ("unknown\n");
372 else
373 grub_printf ("%s (0x%02x)\n",
374 (mode == 0xff ? "active/idle" :
375 mode == 0x80 ? "idle" :
376 mode == 0x00 ? "standby" : "unknown"), mode);
379 /* Check health. */
380 int status = 0;
381 if (health)
383 if (! quiet)
384 grub_printf ("SMART status is: ");
385 int err = grub_hdparm_do_smart_cmd (disk, GRUB_ATA_FEAT_SMART_STATUS);
386 if (! quiet)
387 grub_printf ("%s\n", (err < 0 ? "unknown" :
388 err == 0 ? "OK" : "*BAD*"));
389 status = (err > 0);
392 /* Change power mode. */
393 if (standby_now)
394 grub_hdparm_simple_cmd ("Set disk to standby mode", disk,
395 GRUB_ATA_CMD_STANDBY_IMMEDIATE);
397 if (sleep_now)
398 grub_hdparm_simple_cmd ("Set disk to sleep mode", disk,
399 GRUB_ATA_CMD_SLEEP);
401 grub_disk_close (disk);
403 grub_errno = GRUB_ERR_NONE;
404 return status;
407 static grub_extcmd_t cmd;
409 GRUB_MOD_INIT(hdparm)
411 cmd = grub_register_extcmd ("hdparm", grub_cmd_hdparm,
412 GRUB_COMMAND_FLAG_BOTH,
413 "hdparm [OPTIONS] DISK",
414 "Get/set ATA disk parameters.", options);
417 GRUB_MOD_FINI(hdparm)
419 grub_unregister_extcmd (cmd);