]> git.proxmox.com Git - grub2.git/blob - commands/hdparm.c
* commands/iorw.c: New file.
[grub2.git] / commands / hdparm.c
1 /* hdparm.c - command to get/set ATA disk parameters. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2009 Free Software Foundation, Inc.
5 *
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.
10 *
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.
15 *
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/>.
18 */
19
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>
27 #include <grub/i18n.h>
28
29 static const struct grub_arg_option options[] = {
30 {"apm", 'B', 0, N_("Set Advanced Power Management\n"
31 "(1=low, ..., 254=high, 255=off)."),
32 0, ARG_TYPE_INT},
33 {"power", 'C', 0, N_("Check power mode."), 0, ARG_TYPE_NONE},
34 {"security-freeze", 'F', 0, N_("Freeze ATA security settings until reset."),
35 0, ARG_TYPE_NONE},
36 {"health", 'H', 0, N_("Check SMART health status."), 0, ARG_TYPE_NONE},
37 {"aam", 'M', 0, N_("Set Automatic Acoustic Management\n"
38 "(0=off, 128=quiet, ..., 254=fast)."),
39 0, ARG_TYPE_INT},
40 {"standby-timeout", 'S', 0, N_("Set standby timeout\n"
41 "(0=off, 1=5s, 2=10s, ..., 240=20m, 241=30m, ...)."),
42 0, ARG_TYPE_INT},
43 {"standby", 'y', 0, N_("Set drive to standby mode."), 0, ARG_TYPE_NONE},
44 {"sleep", 'Y', 0, N_("Set drive to sleep mode."), 0, ARG_TYPE_NONE},
45 {"identify", 'i', 0, N_("Print drive identity and settings."),
46 0, ARG_TYPE_NONE},
47 {"dumpid", 'I', 0, N_("Dump contents of ATA IDENTIFY sector."),
48 0, ARG_TYPE_NONE},
49 {"smart", -1, 0, N_("Disable/enable SMART (0/1)."), 0, ARG_TYPE_INT},
50 {"quiet", 'q', 0, N_("Do not print messages."), 0, ARG_TYPE_NONE},
51 {0, 0, 0, 0, 0, 0}
52 };
53
54 enum grub_ata_smart_commands
55 {
56 GRUB_ATA_FEAT_SMART_ENABLE = 0xd8,
57 GRUB_ATA_FEAT_SMART_DISABLE = 0xd9,
58 GRUB_ATA_FEAT_SMART_STATUS = 0xda,
59 };
60
61 static int quiet = 0;
62
63 static grub_err_t
64 grub_hdparm_do_ata_cmd (grub_disk_t disk, grub_uint8_t cmd,
65 grub_uint8_t features, grub_uint8_t sectors,
66 void * buffer, int size)
67 {
68 struct grub_disk_ata_pass_through_parms apt;
69 grub_memset (&apt, 0, sizeof (apt));
70
71 apt.taskfile[GRUB_ATA_REG_CMD] = cmd;
72 apt.taskfile[GRUB_ATA_REG_FEATURES] = features;
73 apt.taskfile[GRUB_ATA_REG_SECTORS] = sectors;
74 apt.buffer = buffer;
75 apt.size = size;
76
77 if (grub_disk_ata_pass_through (disk, &apt))
78 return grub_errno;
79
80 return GRUB_ERR_NONE;
81 }
82
83 static int
84 grub_hdparm_do_check_powermode_cmd (grub_disk_t disk)
85 {
86 struct grub_disk_ata_pass_through_parms apt;
87 grub_memset (&apt, 0, sizeof (apt));
88
89 apt.taskfile[GRUB_ATA_REG_CMD] = GRUB_ATA_CMD_CHECK_POWER_MODE;
90
91 if (grub_disk_ata_pass_through (disk, &apt))
92 return -1;
93
94 return apt.taskfile[GRUB_ATA_REG_SECTORS];
95 }
96
97 static int
98 grub_hdparm_do_smart_cmd (grub_disk_t disk, grub_uint8_t features)
99 {
100 struct grub_disk_ata_pass_through_parms apt;
101 grub_memset (&apt, 0, sizeof (apt));
102
103 apt.taskfile[GRUB_ATA_REG_CMD] = GRUB_ATA_CMD_SMART;
104 apt.taskfile[GRUB_ATA_REG_FEATURES] = features;
105 apt.taskfile[GRUB_ATA_REG_LBAMID] = 0x4f;
106 apt.taskfile[GRUB_ATA_REG_LBAHIGH] = 0xc2;
107
108 if (grub_disk_ata_pass_through (disk, &apt))
109 return -1;
110
111 if (features == GRUB_ATA_FEAT_SMART_STATUS)
112 {
113 if ( apt.taskfile[GRUB_ATA_REG_LBAMID] == 0x4f
114 && apt.taskfile[GRUB_ATA_REG_LBAHIGH] == 0xc2)
115 return 0; /* Good SMART status. */
116 else if ( apt.taskfile[GRUB_ATA_REG_LBAMID] == 0xf4
117 && apt.taskfile[GRUB_ATA_REG_LBAHIGH] == 0x2c)
118 return 1; /* Bad SMART status. */
119 else
120 return -1;
121 }
122 return 0;
123 }
124
125 static grub_err_t
126 grub_hdparm_simple_cmd (const char * msg,
127 grub_disk_t disk, grub_uint8_t cmd)
128 {
129 if (! quiet && msg)
130 grub_printf ("%s", msg);
131
132 grub_err_t err = grub_hdparm_do_ata_cmd (disk, cmd, 0, 0, NULL, 0);
133
134 if (! quiet && msg)
135 grub_printf ("%s\n", ! err ? "" : ": not supported");
136 return err;
137 }
138
139 static grub_err_t
140 grub_hdparm_set_val_cmd (const char * msg, int val,
141 grub_disk_t disk, grub_uint8_t cmd,
142 grub_uint8_t features, grub_uint8_t sectors)
143 {
144 if (! quiet && msg && *msg)
145 {
146 if (val >= 0)
147 grub_printf ("Set %s to %d", msg, val);
148 else
149 grub_printf ("Disable %s", msg);
150 }
151
152 grub_err_t err = grub_hdparm_do_ata_cmd (disk, cmd, features, sectors,
153 NULL, 0);
154
155 if (! quiet && msg)
156 grub_printf ("%s\n", ! err ? "" : ": not supported");
157 return err;
158 }
159
160 static const char *
161 le16_to_char (char *dest, const grub_uint16_t * src16, unsigned bytes)
162 {
163 grub_uint16_t * dest16 = (grub_uint16_t *) dest;
164 unsigned i;
165 for (i = 0; i < bytes / 2; i++)
166 dest16[i] = grub_be_to_cpu16 (src16[i]);
167 return dest;
168 }
169
170 static void
171 grub_hdparm_print_identify (const char * idbuf)
172 {
173 const grub_uint16_t * idw = (const grub_uint16_t *) idbuf;
174
175 /* Print identity strings. */
176 char tmp[40];
177 grub_printf ("Model: \"%.40s\"\n", le16_to_char (tmp, &idw[27], 40));
178 grub_printf ("Firmware: \"%.8s\"\n", le16_to_char (tmp, &idw[23], 8));
179 grub_printf ("Serial: \"%.20s\"\n", le16_to_char (tmp, &idw[10], 20));
180
181 /* Print AAM, APM and SMART settings. */
182 grub_uint16_t features1 = grub_le_to_cpu16 (idw[82]);
183 grub_uint16_t features2 = grub_le_to_cpu16 (idw[83]);
184 grub_uint16_t enabled1 = grub_le_to_cpu16 (idw[85]);
185 grub_uint16_t enabled2 = grub_le_to_cpu16 (idw[86]);
186
187 grub_printf ("Automatic Acoustic Management: ");
188 if (features2 & 0x0200)
189 {
190 if (enabled2 & 0x0200)
191 {
192 grub_uint16_t aam = grub_le_to_cpu16 (idw[94]);
193 grub_printf ("%u (128=quiet, ..., 254=fast, recommended=%u)\n",
194 aam & 0xff, (aam >> 8) & 0xff);
195 }
196 else
197 grub_printf ("disabled\n");
198 }
199 else
200 grub_printf ("not supported\n");
201
202 grub_printf ("Advanced Power Management: ");
203 if (features2 & 0x0008)
204 {
205 if (enabled2 & 0x0008)
206 grub_printf ("%u (1=low, ..., 254=high)\n",
207 grub_le_to_cpu16 (idw[91]) & 0xff);
208 else
209 grub_printf ("disabled\n");
210 }
211 else
212 grub_printf ("not supported\n");
213
214 grub_printf ("SMART Feature Set: ");
215 if (features1 & 0x0001)
216 grub_printf ("%sabled\n", (enabled1 & 0x0001 ? "en" : "dis"));
217 else
218 grub_printf ("not supported\n");
219
220 /* Print security settings. */
221 grub_uint16_t security = grub_le_to_cpu16 (idw[128]);
222
223 grub_printf ("ATA Security: ");
224 if (security & 0x0001)
225 grub_printf ("%s, %s, %s, %s\n",
226 (security & 0x0002 ? "ENABLED" : "disabled"),
227 (security & 0x0004 ? "**LOCKED**" : "not locked"),
228 (security & 0x0008 ? "frozen" : "NOT FROZEN"),
229 (security & 0x0010 ? "COUNT EXPIRED" : "count not expired"));
230 else
231 grub_printf ("not supported\n");
232 }
233
234 static void
235 grub_hdparm_print_standby_tout (int timeout)
236 {
237 if (timeout == 0)
238 grub_printf ("off");
239 else if (timeout <= 252 || timeout == 255)
240 {
241 int h = 0, m = 0 , s = 0;
242 if (timeout == 255)
243 {
244 m = 21;
245 s = 15;
246 }
247 else if (timeout == 252)
248 m = 21;
249 else if (timeout <= 240)
250 {
251 s = timeout * 5;
252 m = s / 60;
253 s %= 60;
254 }
255 else
256 {
257 m = (timeout - 240) * 30;
258 h = m / 60;
259 m %= 60;
260 }
261 grub_printf ("%02d:%02d:%02d", h, m, s);
262 }
263 else
264 grub_printf ("invalid or vendor-specific");
265 }
266
267 static int get_int_arg (const struct grub_arg_list *state)
268 {
269 return (state->set ? (int)grub_strtoul (state->arg, 0, 0) : -1);
270 }
271
272 static grub_err_t
273 grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
274 {
275 struct grub_arg_list *state = cmd->state;
276
277 /* Check command line. */
278 if (argc != 1)
279 return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing device name argument");
280
281 grub_size_t len = grub_strlen (args[0]);
282 if (! (args[0][0] == '(' && args[0][len - 1] == ')'))
283 return grub_error (GRUB_ERR_BAD_ARGUMENT, "argument is not a device name");
284 args[0][len - 1] = 0;
285
286 if (! grub_disk_ata_pass_through)
287 return grub_error (GRUB_ERR_BAD_ARGUMENT, "ATA pass through not available");
288
289 int i = 0;
290 int apm = get_int_arg (&state[i++]);
291 int power = state[i++].set;
292 int sec_freeze = state[i++].set;
293 int health = state[i++].set;
294 int aam = get_int_arg (&state[i++]);
295 int standby_tout = get_int_arg (&state[i++]);
296 int standby_now = state[i++].set;
297 int sleep_now = state[i++].set;
298 int ident = state[i++].set;
299 int dumpid = state[i++].set;
300 int enable_smart = get_int_arg (&state[i++]);
301 quiet = state[i++].set;
302
303 /* Open disk. */
304 grub_disk_t disk = grub_disk_open (&args[0][1]);
305 if (! disk)
306 return grub_errno;
307
308 if (disk->partition)
309 {
310 grub_disk_close (disk);
311 return grub_error (GRUB_ERR_BAD_ARGUMENT, "partition not allowed");
312 }
313
314 /* Change settings. */
315 if (aam >= 0)
316 grub_hdparm_set_val_cmd ("Automatic Acoustic Management", (aam ? aam : -1),
317 disk, GRUB_ATA_CMD_SET_FEATURES, (aam ? 0x42 : 0xc2), aam);
318
319 if (apm >= 0)
320 grub_hdparm_set_val_cmd ("Advanced Power Management",
321 (apm != 255 ? apm : -1), disk, GRUB_ATA_CMD_SET_FEATURES,
322 (apm != 255 ? 0x05 : 0x85), (apm != 255 ? apm : 0));
323
324 if (standby_tout >= 0)
325 {
326 if (! quiet)
327 {
328 grub_printf ("Set standby timeout to %d (", standby_tout);
329 grub_hdparm_print_standby_tout (standby_tout);
330 grub_printf (")");
331 }
332 /* The IDLE cmd sets disk to idle mode and configures standby timer. */
333 grub_hdparm_set_val_cmd ("", -1, disk, GRUB_ATA_CMD_IDLE, 0, standby_tout);
334 }
335
336 if (enable_smart >= 0)
337 {
338 if (! quiet)
339 grub_printf ("%sable SMART operations", (enable_smart ? "En" : "Dis"));
340 int err = grub_hdparm_do_smart_cmd (disk, (enable_smart ?
341 GRUB_ATA_FEAT_SMART_ENABLE : GRUB_ATA_FEAT_SMART_DISABLE));
342 if (! quiet)
343 grub_printf ("%s\n", err ? ": not supported" : "");
344 }
345
346 if (sec_freeze)
347 grub_hdparm_simple_cmd ("Freeze security settings", disk,
348 GRUB_ATA_CMD_SECURITY_FREEZE_LOCK);
349
350 /* Print/dump IDENTIFY. */
351 if (ident || dumpid)
352 {
353 char buf[GRUB_DISK_SECTOR_SIZE];
354 if (grub_hdparm_do_ata_cmd (disk, GRUB_ATA_CMD_IDENTIFY_DEVICE,
355 0, 0, buf, sizeof (buf)))
356 grub_printf ("Cannot read ATA IDENTIFY data\n");
357 else
358 {
359 if (ident)
360 grub_hdparm_print_identify (buf);
361 if (dumpid)
362 hexdump (0, buf, sizeof (buf));
363 }
364 }
365
366 /* Check power mode. */
367 if (power)
368 {
369 grub_printf ("Disk power mode is: ");
370 int mode = grub_hdparm_do_check_powermode_cmd (disk);
371 if (mode < 0)
372 grub_printf ("unknown\n");
373 else
374 grub_printf ("%s (0x%02x)\n",
375 (mode == 0xff ? "active/idle" :
376 mode == 0x80 ? "idle" :
377 mode == 0x00 ? "standby" : "unknown"), mode);
378 }
379
380 /* Check health. */
381 int status = 0;
382 if (health)
383 {
384 if (! quiet)
385 grub_printf ("SMART status is: ");
386 int err = grub_hdparm_do_smart_cmd (disk, GRUB_ATA_FEAT_SMART_STATUS);
387 if (! quiet)
388 grub_printf ("%s\n", (err < 0 ? "unknown" :
389 err == 0 ? "OK" : "*BAD*"));
390 status = (err > 0);
391 }
392
393 /* Change power mode. */
394 if (standby_now)
395 grub_hdparm_simple_cmd ("Set disk to standby mode", disk,
396 GRUB_ATA_CMD_STANDBY_IMMEDIATE);
397
398 if (sleep_now)
399 grub_hdparm_simple_cmd ("Set disk to sleep mode", disk,
400 GRUB_ATA_CMD_SLEEP);
401
402 grub_disk_close (disk);
403
404 grub_errno = GRUB_ERR_NONE;
405 return status;
406 }
407
408 static grub_extcmd_t cmd;
409
410 GRUB_MOD_INIT(hdparm)
411 {
412 cmd = grub_register_extcmd ("hdparm", grub_cmd_hdparm,
413 GRUB_COMMAND_FLAG_BOTH,
414 N_("[OPTIONS] DISK"),
415 N_("Get/set ATA disk parameters."), options);
416 }
417
418 GRUB_MOD_FINI(hdparm)
419 {
420 grub_unregister_extcmd (cmd);
421 }