hdparm.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  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. #include <grub/ata.h>
  20. #include <grub/scsi.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. GRUB_MOD_LICENSE ("GPLv3+");
  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_("Display 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_("Display 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_("Show raw 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. enum grub_ata_smart_commands
  54. {
  55. GRUB_ATA_FEAT_SMART_ENABLE = 0xd8,
  56. GRUB_ATA_FEAT_SMART_DISABLE = 0xd9,
  57. GRUB_ATA_FEAT_SMART_STATUS = 0xda,
  58. };
  59. static int quiet = 0;
  60. static grub_err_t
  61. grub_hdparm_do_ata_cmd (grub_ata_t ata, grub_uint8_t cmd,
  62. grub_uint8_t features, grub_uint8_t sectors,
  63. void * buffer, int size)
  64. {
  65. struct grub_disk_ata_pass_through_parms apt;
  66. grub_memset (&apt, 0, sizeof (apt));
  67. apt.taskfile.cmd = cmd;
  68. apt.taskfile.features = features;
  69. apt.taskfile.sectors = sectors;
  70. apt.taskfile.disk = 0xE0;
  71. apt.buffer = buffer;
  72. apt.size = size;
  73. if (ata->dev->readwrite (ata, &apt, 0))
  74. return grub_errno;
  75. return GRUB_ERR_NONE;
  76. }
  77. static int
  78. grub_hdparm_do_check_powermode_cmd (grub_ata_t ata)
  79. {
  80. struct grub_disk_ata_pass_through_parms apt;
  81. grub_memset (&apt, 0, sizeof (apt));
  82. apt.taskfile.cmd = GRUB_ATA_CMD_CHECK_POWER_MODE;
  83. apt.taskfile.disk = 0xE0;
  84. if (ata->dev->readwrite (ata, &apt, 0))
  85. return -1;
  86. return apt.taskfile.sectors;
  87. }
  88. static int
  89. grub_hdparm_do_smart_cmd (grub_ata_t ata, grub_uint8_t features)
  90. {
  91. struct grub_disk_ata_pass_through_parms apt;
  92. grub_memset (&apt, 0, sizeof (apt));
  93. apt.taskfile.cmd = GRUB_ATA_CMD_SMART;
  94. apt.taskfile.features = features;
  95. apt.taskfile.lba_mid = 0x4f;
  96. apt.taskfile.lba_high = 0xc2;
  97. apt.taskfile.disk = 0xE0;
  98. if (ata->dev->readwrite (ata, &apt, 0))
  99. return -1;
  100. if (features == GRUB_ATA_FEAT_SMART_STATUS)
  101. {
  102. if ( apt.taskfile.lba_mid == 0x4f
  103. && apt.taskfile.lba_high == 0xc2)
  104. return 0; /* Good SMART status. */
  105. else if ( apt.taskfile.lba_mid == 0xf4
  106. && apt.taskfile.lba_high == 0x2c)
  107. return 1; /* Bad SMART status. */
  108. else
  109. return -1;
  110. }
  111. return 0;
  112. }
  113. static grub_err_t
  114. grub_hdparm_simple_cmd (const char * msg,
  115. grub_ata_t ata, grub_uint8_t cmd)
  116. {
  117. if (! quiet && msg)
  118. grub_printf ("%s", msg);
  119. grub_err_t err = grub_hdparm_do_ata_cmd (ata, cmd, 0, 0, NULL, 0);
  120. if (! quiet && msg)
  121. grub_printf ("%s\n", ! err ? "" : ": not supported");
  122. return err;
  123. }
  124. static grub_err_t
  125. grub_hdparm_set_val_cmd (const char * msg, int val,
  126. grub_ata_t ata, grub_uint8_t cmd,
  127. grub_uint8_t features, grub_uint8_t sectors)
  128. {
  129. if (! quiet && msg && *msg)
  130. {
  131. if (val >= 0)
  132. grub_printf ("Set %s to %d", msg, val);
  133. else
  134. grub_printf ("Disable %s", msg);
  135. }
  136. grub_err_t err = grub_hdparm_do_ata_cmd (ata, cmd, features, sectors,
  137. NULL, 0);
  138. if (! quiet && msg)
  139. grub_printf ("%s\n", ! err ? "" : ": not supported");
  140. return err;
  141. }
  142. static const char *
  143. le16_to_char (grub_uint16_t *dest, const grub_uint16_t * src16, unsigned bytes)
  144. {
  145. unsigned i;
  146. for (i = 0; i < bytes / 2; i++)
  147. dest[i] = grub_swap_bytes16 (src16[i]);
  148. dest[i] = 0;
  149. return (char *) dest;
  150. }
  151. static void
  152. grub_hdparm_print_identify (const grub_uint16_t * idw)
  153. {
  154. /* Print identity strings. */
  155. grub_uint16_t tmp[21];
  156. grub_printf ("Model: \"%.40s\"\n", le16_to_char (tmp, &idw[27], 40));
  157. grub_printf ("Firmware: \"%.8s\"\n", le16_to_char (tmp, &idw[23], 8));
  158. grub_printf ("Serial: \"%.20s\"\n", le16_to_char (tmp, &idw[10], 20));
  159. /* Print AAM, APM and SMART settings. */
  160. grub_uint16_t features1 = grub_le_to_cpu16 (idw[82]);
  161. grub_uint16_t features2 = grub_le_to_cpu16 (idw[83]);
  162. grub_uint16_t enabled1 = grub_le_to_cpu16 (idw[85]);
  163. grub_uint16_t enabled2 = grub_le_to_cpu16 (idw[86]);
  164. grub_printf ("Automatic Acoustic Management: ");
  165. if (features2 & 0x0200)
  166. {
  167. if (enabled2 & 0x0200)
  168. {
  169. grub_uint16_t aam = grub_le_to_cpu16 (idw[94]);
  170. grub_printf ("%u (128=quiet, ..., 254=fast, recommended=%u)\n",
  171. aam & 0xff, (aam >> 8) & 0xff);
  172. }
  173. else
  174. grub_printf ("disabled\n");
  175. }
  176. else
  177. grub_printf ("not supported\n");
  178. grub_printf ("Advanced Power Management: ");
  179. if (features2 & 0x0008)
  180. {
  181. if (enabled2 & 0x0008)
  182. grub_printf ("%u (1=low, ..., 254=high)\n",
  183. grub_le_to_cpu16 (idw[91]) & 0xff);
  184. else
  185. grub_printf ("disabled\n");
  186. }
  187. else
  188. grub_printf ("not supported\n");
  189. grub_printf ("SMART Feature Set: ");
  190. if (features1 & 0x0001)
  191. grub_printf ("%sabled\n", (enabled1 & 0x0001 ? "en" : "dis"));
  192. else
  193. grub_printf ("not supported\n");
  194. /* Print security settings. */
  195. grub_uint16_t security = grub_le_to_cpu16 (idw[128]);
  196. grub_printf ("ATA Security: ");
  197. if (security & 0x0001)
  198. grub_printf ("%s, %s, %s, %s\n",
  199. (security & 0x0002 ? "ENABLED" : "disabled"),
  200. (security & 0x0004 ? "**LOCKED**" : "not locked"),
  201. (security & 0x0008 ? "frozen" : "NOT FROZEN"),
  202. (security & 0x0010 ? "COUNT EXPIRED" : "count not expired"));
  203. else
  204. grub_printf ("not supported\n");
  205. }
  206. static void
  207. grub_hdparm_print_standby_tout (int timeout)
  208. {
  209. if (timeout == 0)
  210. grub_printf ("off");
  211. else if (timeout <= 252 || timeout == 255)
  212. {
  213. int h = 0, m = 0 , s = 0;
  214. if (timeout == 255)
  215. {
  216. m = 21;
  217. s = 15;
  218. }
  219. else if (timeout == 252)
  220. m = 21;
  221. else if (timeout <= 240)
  222. {
  223. s = timeout * 5;
  224. m = s / 60;
  225. s %= 60;
  226. }
  227. else
  228. {
  229. m = (timeout - 240) * 30;
  230. h = m / 60;
  231. m %= 60;
  232. }
  233. grub_printf ("%02d:%02d:%02d", h, m, s);
  234. }
  235. else
  236. grub_printf ("invalid or vendor-specific");
  237. }
  238. static int get_int_arg (const struct grub_arg_list *state)
  239. {
  240. return (state->set ? (int)grub_strtoul (state->arg, 0, 0) : -1);
  241. }
  242. static grub_err_t
  243. grub_cmd_hdparm (grub_extcmd_context_t ctxt, int argc, char **args)
  244. {
  245. struct grub_arg_list *state = ctxt->state;
  246. struct grub_ata *ata;
  247. const char *diskname;
  248. /* Check command line. */
  249. if (argc != 1)
  250. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
  251. if (args[0][0] == '(')
  252. {
  253. grub_size_t len = grub_strlen (args[0]);
  254. if (args[0][len - 1] == ')')
  255. args[0][len - 1] = 0;
  256. diskname = &args[0][1];
  257. }
  258. else
  259. diskname = &args[0][0];
  260. int i = 0;
  261. int apm = get_int_arg (&state[i++]);
  262. int power = state[i++].set;
  263. int sec_freeze = state[i++].set;
  264. int health = state[i++].set;
  265. int aam = get_int_arg (&state[i++]);
  266. int standby_tout = get_int_arg (&state[i++]);
  267. int standby_now = state[i++].set;
  268. int sleep_now = state[i++].set;
  269. int ident = state[i++].set;
  270. int dumpid = state[i++].set;
  271. int enable_smart = get_int_arg (&state[i++]);
  272. quiet = state[i++].set;
  273. /* Open disk. */
  274. grub_disk_t disk = grub_disk_open (diskname);
  275. if (! disk)
  276. return grub_errno;
  277. switch (disk->dev->id)
  278. {
  279. case GRUB_DISK_DEVICE_ATA_ID:
  280. ata = disk->data;
  281. break;
  282. case GRUB_DISK_DEVICE_SCSI_ID:
  283. if (((disk->id >> GRUB_SCSI_ID_SUBSYSTEM_SHIFT) & 0xFF)
  284. == GRUB_SCSI_SUBSYSTEM_PATA
  285. || (((disk->id >> GRUB_SCSI_ID_SUBSYSTEM_SHIFT) & 0xFF)
  286. == GRUB_SCSI_SUBSYSTEM_AHCI))
  287. {
  288. ata = ((struct grub_scsi *) disk->data)->data;
  289. break;
  290. }
  291. /* FALLTHROUGH */
  292. default:
  293. grub_disk_close (disk);
  294. return grub_error (GRUB_ERR_IO, "not an ATA device");
  295. }
  296. /* Change settings. */
  297. if (aam >= 0)
  298. grub_hdparm_set_val_cmd ("Automatic Acoustic Management", (aam ? aam : -1),
  299. ata, GRUB_ATA_CMD_SET_FEATURES,
  300. (aam ? 0x42 : 0xc2), aam);
  301. if (apm >= 0)
  302. grub_hdparm_set_val_cmd ("Advanced Power Management",
  303. (apm != 255 ? apm : -1), ata,
  304. GRUB_ATA_CMD_SET_FEATURES,
  305. (apm != 255 ? 0x05 : 0x85),
  306. (apm != 255 ? apm : 0));
  307. if (standby_tout >= 0)
  308. {
  309. if (! quiet)
  310. {
  311. grub_printf ("Set standby timeout to %d (", standby_tout);
  312. grub_hdparm_print_standby_tout (standby_tout);
  313. grub_printf (")");
  314. }
  315. /* The IDLE cmd sets disk to idle mode and configures standby timer. */
  316. grub_hdparm_set_val_cmd ("", -1, ata, GRUB_ATA_CMD_IDLE, 0, standby_tout);
  317. }
  318. if (enable_smart >= 0)
  319. {
  320. if (! quiet)
  321. grub_printf ("%sable SMART operations", (enable_smart ? "En" : "Dis"));
  322. int err = grub_hdparm_do_smart_cmd (ata, (enable_smart ?
  323. GRUB_ATA_FEAT_SMART_ENABLE : GRUB_ATA_FEAT_SMART_DISABLE));
  324. if (! quiet)
  325. grub_printf ("%s\n", err ? ": not supported" : "");
  326. }
  327. if (sec_freeze)
  328. grub_hdparm_simple_cmd ("Freeze security settings", ata,
  329. GRUB_ATA_CMD_SECURITY_FREEZE_LOCK);
  330. /* Print/dump IDENTIFY. */
  331. if (ident || dumpid)
  332. {
  333. grub_uint16_t buf[GRUB_DISK_SECTOR_SIZE / 2];
  334. if (grub_hdparm_do_ata_cmd (ata, GRUB_ATA_CMD_IDENTIFY_DEVICE,
  335. 0, 0, buf, sizeof (buf)))
  336. grub_printf ("Cannot read ATA IDENTIFY data\n");
  337. else
  338. {
  339. if (ident)
  340. grub_hdparm_print_identify (buf);
  341. if (dumpid)
  342. hexdump (0, (char *) buf, sizeof (buf));
  343. }
  344. }
  345. /* Check power mode. */
  346. if (power)
  347. {
  348. grub_printf ("Disk power mode is: ");
  349. int mode = grub_hdparm_do_check_powermode_cmd (ata);
  350. if (mode < 0)
  351. grub_printf ("unknown\n");
  352. else
  353. grub_printf ("%s (0x%02x)\n",
  354. (mode == 0xff ? "active/idle" :
  355. mode == 0x80 ? "idle" :
  356. mode == 0x00 ? "standby" : "unknown"), mode);
  357. }
  358. /* Check health. */
  359. int status = 0;
  360. if (health)
  361. {
  362. if (! quiet)
  363. grub_printf ("SMART status is: ");
  364. int err = grub_hdparm_do_smart_cmd (ata, GRUB_ATA_FEAT_SMART_STATUS);
  365. if (! quiet)
  366. grub_printf ("%s\n", (err < 0 ? "unknown" :
  367. err == 0 ? "OK" : "*BAD*"));
  368. status = (err > 0);
  369. }
  370. /* Change power mode. */
  371. if (standby_now)
  372. grub_hdparm_simple_cmd ("Set disk to standby mode", ata,
  373. GRUB_ATA_CMD_STANDBY_IMMEDIATE);
  374. if (sleep_now)
  375. grub_hdparm_simple_cmd ("Set disk to sleep mode", ata,
  376. GRUB_ATA_CMD_SLEEP);
  377. grub_disk_close (disk);
  378. grub_errno = GRUB_ERR_NONE;
  379. return status;
  380. }
  381. static grub_extcmd_t cmd;
  382. GRUB_MOD_INIT(hdparm)
  383. {
  384. cmd = grub_register_extcmd ("hdparm", grub_cmd_hdparm, 0,
  385. N_("[OPTIONS] DISK"),
  386. N_("Get/set ATA disk parameters."), options);
  387. }
  388. GRUB_MOD_FINI(hdparm)
  389. {
  390. grub_unregister_extcmd (cmd);
  391. }