be nearer to original
[grub2/phcoder.git] / commands / hdparm.c
blob2dc228f6f87f73a2f2ff2df01db7610b4c809803
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 grub_err_t err;
130 if (! quiet && msg)
131 grub_printf ("%s", msg);
133 err = grub_hdparm_do_ata_cmd (disk, cmd, 0, 0, NULL, 0);
135 if (! quiet && msg)
136 grub_printf ("%s\n", ! err ? "" : ": not supported");
137 return err;
140 static grub_err_t
141 grub_hdparm_set_val_cmd (const char * msg, int val,
142 grub_disk_t disk, grub_uint8_t cmd,
143 grub_uint8_t features, grub_uint8_t sectors)
145 grub_err_t err;
147 if (! quiet && msg && *msg)
149 if (val >= 0)
150 grub_printf ("Set %s to %d", msg, val);
151 else
152 grub_printf ("Disable %s", msg);
155 err = grub_hdparm_do_ata_cmd (disk, cmd, features, sectors, NULL, 0);
157 if (! quiet && msg)
158 grub_printf ("%s\n", ! err ? "" : ": not supported");
159 return err;
162 static const char *
163 le16_to_char (char *dest, const grub_uint16_t * src16, unsigned bytes)
165 grub_uint16_t * dest16 = (grub_uint16_t *) dest;
166 unsigned i;
167 for (i = 0; i < bytes / 2; i++)
168 dest16[i] = grub_be_to_cpu16 (src16[i]);
169 return dest;
172 static void
173 grub_hdparm_print_identify (const char * idbuf)
175 const grub_uint16_t * idw = (const grub_uint16_t *) idbuf;
176 grub_uint16_t features1;
177 grub_uint16_t features2;
178 grub_uint16_t enabled1;
179 grub_uint16_t enabled2;
180 grub_uint16_t security;
181 char tmp[40];
183 /* Print identity strings. */
184 grub_printf ("Model: \"%.40s\"\n", le16_to_char (tmp, &idw[27], 40));
185 grub_printf ("Firmware: \"%.8s\"\n", le16_to_char (tmp, &idw[23], 8));
186 grub_printf ("Serial: \"%.20s\"\n", le16_to_char (tmp, &idw[10], 20));
188 /* Print AAM, APM and SMART settings. */
189 features1 = grub_le_to_cpu16 (idw[82]);
190 features2 = grub_le_to_cpu16 (idw[83]);
191 enabled1 = grub_le_to_cpu16 (idw[85]);
192 enabled2 = grub_le_to_cpu16 (idw[86]);
194 grub_printf ("Automatic Acoustic Management: ");
195 if (features2 & 0x0200)
197 if (enabled2 & 0x0200)
199 grub_uint16_t aam = grub_le_to_cpu16 (idw[94]);
200 grub_printf ("%u (128=quiet, ..., 254=fast, recommended=%u)\n",
201 aam & 0xff, (aam >> 8) & 0xff);
203 else
204 grub_printf ("disabled\n");
206 else
207 grub_printf ("not supported\n");
209 grub_printf ("Advanced Power Management: ");
210 if (features2 & 0x0008)
212 if (enabled2 & 0x0008)
213 grub_printf ("%u (1=low, ..., 254=high)\n",
214 grub_le_to_cpu16 (idw[91]) & 0xff);
215 else
216 grub_printf ("disabled\n");
218 else
219 grub_printf ("not supported\n");
221 grub_printf ("SMART Feature Set: ");
222 if (features1 & 0x0001)
223 grub_printf ("%sabled\n", (enabled1 & 0x0001 ? "en" : "dis"));
224 else
225 grub_printf ("not supported\n");
227 /* Print security settings. */
228 security = grub_le_to_cpu16 (idw[128]);
230 grub_printf ("ATA Security: ");
231 if (security & 0x0001)
232 grub_printf ("%s, %s, %s, %s\n",
233 (security & 0x0002 ? "ENABLED" : "disabled"),
234 (security & 0x0004 ? "**LOCKED**" : "not locked"),
235 (security & 0x0008 ? "frozen" : "NOT FROZEN"),
236 (security & 0x0010 ? "COUNT EXPIRED" : "count not expired"));
237 else
238 grub_printf ("not supported\n");
241 static void
242 grub_hdparm_print_standby_tout (int timeout)
244 if (timeout == 0)
245 grub_printf ("off");
246 else if (timeout <= 252 || timeout == 255)
248 int h = 0, m = 0 , s = 0;
249 if (timeout == 255)
251 m = 21;
252 s = 15;
254 else if (timeout == 252)
255 m = 21;
256 else if (timeout <= 240)
258 s = timeout * 5;
259 m = s / 60;
260 s %= 60;
262 else
264 m = (timeout - 240) * 30;
265 h = m / 60;
266 m %= 60;
268 grub_printf ("%02d:%02d:%02d", h, m, s);
270 else
271 grub_printf ("invalid or vendor-specific");
274 static int get_int_arg (const struct grub_arg_list *state)
276 return (state->set ? (int)grub_strtoul (state->arg, 0, 0) : -1);
279 static grub_err_t
280 grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
282 struct grub_arg_list *state = cmd->state;
283 grub_size_t len;
284 int i;
285 int apm;
286 int power;
287 int sec_freeze;
288 int health;
289 int aam;
290 int standby_tout;
291 int standby_now;
292 int sleep_now;
293 int ident;
294 int dumpid;
295 int enable_smart;
296 grub_disk_t disk;
297 int status = 0;
299 /* Check command line. */
300 if (argc != 1)
301 return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing device name argument");
303 len = grub_strlen (args[0]);
304 if (! (args[0][0] == '(' && args[0][len - 1] == ')'))
305 return grub_error (GRUB_ERR_BAD_ARGUMENT, "argument is not a device name");
306 args[0][len - 1] = 0;
308 if (! grub_disk_ata_pass_through)
309 return grub_error (GRUB_ERR_BAD_ARGUMENT, "ATA pass through not available");
311 i = 0;
312 apm = get_int_arg (&state[i++]);
313 power = state[i++].set;
314 sec_freeze = state[i++].set;
315 health = state[i++].set;
316 aam = get_int_arg (&state[i++]);
317 standby_tout = get_int_arg (&state[i++]);
318 standby_now = state[i++].set;
319 sleep_now = state[i++].set;
320 ident = state[i++].set;
321 dumpid = state[i++].set;
322 enable_smart = get_int_arg (&state[i++]);
323 quiet = state[i++].set;
325 /* Open disk. */
326 disk = grub_disk_open (&args[0][1]);
327 if (! disk)
328 return grub_errno;
330 if (disk->partition)
332 grub_disk_close (disk);
333 return grub_error (GRUB_ERR_BAD_ARGUMENT, "partition not allowed");
336 /* Change settings. */
337 if (aam >= 0)
338 grub_hdparm_set_val_cmd ("Automatic Acoustic Management", (aam ? aam : -1),
339 disk, GRUB_ATA_CMD_SET_FEATURES, (aam ? 0x42 : 0xc2), aam);
341 if (apm >= 0)
342 grub_hdparm_set_val_cmd ("Advanced Power Management",
343 (apm != 255 ? apm : -1), disk, GRUB_ATA_CMD_SET_FEATURES,
344 (apm != 255 ? 0x05 : 0x85), (apm != 255 ? apm : 0));
346 if (standby_tout >= 0)
348 if (! quiet)
350 grub_printf ("Set standby timeout to %d (", standby_tout);
351 grub_hdparm_print_standby_tout (standby_tout);
352 grub_printf (")");
354 /* The IDLE cmd sets disk to idle mode and configures standby timer. */
355 grub_hdparm_set_val_cmd ("", -1, disk, GRUB_ATA_CMD_IDLE, 0, standby_tout);
358 if (enable_smart >= 0)
360 int err;
362 if (! quiet)
363 grub_printf ("%sable SMART operations", (enable_smart ? "En" : "Dis"));
364 err = grub_hdparm_do_smart_cmd (disk, (enable_smart ?
365 GRUB_ATA_FEAT_SMART_ENABLE
366 : GRUB_ATA_FEAT_SMART_DISABLE));
367 if (! quiet)
368 grub_printf ("%s\n", err ? ": not supported" : "");
371 if (sec_freeze)
372 grub_hdparm_simple_cmd ("Freeze security settings", disk,
373 GRUB_ATA_CMD_SECURITY_FREEZE_LOCK);
375 /* Print/dump IDENTIFY. */
376 if (ident || dumpid)
378 char buf[GRUB_DISK_SECTOR_SIZE];
379 if (grub_hdparm_do_ata_cmd (disk, GRUB_ATA_CMD_IDENTIFY_DEVICE,
380 0, 0, buf, sizeof (buf)))
381 grub_printf ("Cannot read ATA IDENTIFY data\n");
382 else
384 if (ident)
385 grub_hdparm_print_identify (buf);
386 if (dumpid)
387 hexdump (0, buf, sizeof (buf));
391 /* Check power mode. */
392 if (power)
394 int mode;
396 grub_printf ("Disk power mode is: ");
397 mode = grub_hdparm_do_check_powermode_cmd (disk);
398 if (mode < 0)
399 grub_printf ("unknown\n");
400 else
401 grub_printf ("%s (0x%02x)\n",
402 (mode == 0xff ? "active/idle" :
403 mode == 0x80 ? "idle" :
404 mode == 0x00 ? "standby" : "unknown"), mode);
407 /* Check health. */
408 if (health)
410 int err;
412 if (! quiet)
413 grub_printf ("SMART status is: ");
414 err = grub_hdparm_do_smart_cmd (disk, GRUB_ATA_FEAT_SMART_STATUS);
415 if (! quiet)
416 grub_printf ("%s\n", (err < 0 ? "unknown" :
417 err == 0 ? "OK" : "*BAD*"));
418 status = (err > 0);
421 /* Change power mode. */
422 if (standby_now)
423 grub_hdparm_simple_cmd ("Set disk to standby mode", disk,
424 GRUB_ATA_CMD_STANDBY_IMMEDIATE);
426 if (sleep_now)
427 grub_hdparm_simple_cmd ("Set disk to sleep mode", disk,
428 GRUB_ATA_CMD_SLEEP);
430 grub_disk_close (disk);
432 grub_errno = GRUB_ERR_NONE;
433 return status;
436 static grub_extcmd_t cmd;
438 GRUB_MOD_INIT(hdparm)
440 cmd = grub_register_extcmd ("hdparm", grub_cmd_hdparm,
441 GRUB_COMMAND_FLAG_BOTH,
442 "hdparm [OPTIONS] DISK",
443 "Get/set ATA disk parameters.", options);
446 GRUB_MOD_FINI(hdparm)
448 grub_unregister_extcmd (cmd);