Index: sbin/atactl/atactl.8 =================================================================== RCS file: /cvsroot/src/sbin/atactl/atactl.8,v retrieving revision 1.23 diff -p -u -r1.23 atactl.8 --- sbin/atactl/atactl.8 30 Apr 2008 13:10:52 -0000 1.23 +++ sbin/atactl/atactl.8 9 Jan 2013 21:40:53 -0000 @@ -27,7 +27,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd November 18, 2007 +.Dd January 9, 2013 .Dt ATACTL 8 .Os .Sh NAME @@ -160,7 +160,7 @@ Prints the error log. .It Ar selftest-log Prints the self-test log. .El -.It Cm security Bq Ar freeze | status +.It Cm security Bq Ar status | freeze | setpass | unlock | disable | erase Controls .Dq security (password protection) features of modern ATA drives. @@ -175,11 +175,46 @@ sense to issue the .Dq freeze command early in the boot process. .Bl -tag -width freezeXX -.It Ar freeze -freezes the drive's security status .It Ar status displays the drive's security status +.It Ar freeze +freezes the drive's security status +.It Ar setpass Bq user | master +sets the drive's user or master password +.It Ar unlock Bq user | master +unlocks a password-protected drive +.It Ar disable Bq user | master +disables password protection +.It Ar erase Bq user | master +erases the device and clears security state, using enhanced erasure if +the drive supports it; may take a long time to run .El +.Pp +Note that to erase a drive, it must have a password set and be +unfrozen. +If you can't persuade your firmware to leave the drive unfrozen on +boot, but it is a SATA drive, say +.Pa wd2 +at +.Pa atabus3 , +that you can safely physically disconnect and reconnect, then you may +be able to use SATA hot-plug to work around this: first run +.Bd -literal -offset indent +# drvctl -d wd2 +.Ed +.Pp +Then physically disconnect and reconnect the drive, and run +.Bd -literal -offset indent +# drvctl -r -a ata_hl atabus3 +.Ed +.Pp +After this, check that the security status does not list +.Dq frozen : +.Bd -literal -offset indent +# atactl wd2 security status + supported +# +.Ed .El .Sh BUS COMMANDS The following commands may be used on IDE and ATA busses. @@ -190,10 +225,29 @@ Reset the bus. This will reset all ATA devices present on the bus. Any ATAPI device with pending commands will also be reset. .El +.Sh EXAMPLES +To erase +.Pa wd2 +which is currently unfrozen and has no password set: +.Bd -literal -offset indent +# atactl wd2 security status + supported +# atactl wd2 security setpass user +Password: +Confirm password: +# atactl wd2 security status + supported + enabled +# atactl wd2 security erase user +Password: +Erasing may take up to 0h 2m 0s... +# +.Ed .Sh SEE ALSO .Xr ioctl 2 , .Xr wd 4 , .Xr dkctl 8 , +.Xr drvctl 8 , .Xr scsictl 8 .Sh HISTORY The @@ -211,3 +265,10 @@ command written by Jason R. Thorpe. The output from the .Cm identify command is rather ugly. +.Pp +Support for master passwords is not implemented. +.Pp +The +.Nx +kernel behaves poorly with drives that have passwords set and are +locked. Index: sbin/atactl/atactl.c =================================================================== RCS file: /cvsroot/src/sbin/atactl/atactl.c,v retrieving revision 1.67 diff -p -u -r1.67 atactl.c --- sbin/atactl/atactl.c 19 Oct 2012 17:09:07 -0000 1.67 +++ sbin/atactl/atactl.c 9 Jan 2013 21:40:53 -0000 @@ -44,6 +44,7 @@ __RCSID("$NetBSD: atactl.c,v 1.67 2012/1 #include #include #include +#include #include #include #include @@ -144,7 +145,9 @@ static const struct command device_comma { "smart", "enable|disable|status|offline #|error-log|selftest-log", device_smart }, - { "security", "freeze|status", device_security }, + { "security", + "status|freeze|[setpass|unlock|disable|erase] [user|master]", + device_security }, { NULL, NULL, NULL }, }; @@ -851,6 +854,49 @@ extract_string(char *buf, size_t bufmax, buf[j] = '\0'; } +static void +compute_capacity(const struct ataparams *inqbuf, uint64_t *capacityp, + uint64_t *sectorsp, uint32_t *secsizep) +{ + uint64_t capacity; + uint64_t sectors; + uint32_t secsize; + + if (inqbuf->atap_cmd2_en != 0 && inqbuf->atap_cmd2_en != 0xffff && + inqbuf->atap_cmd2_en & ATA_CMD2_LBA48) { + sectors = + ((uint64_t)inqbuf->atap_max_lba[3] << 48) | + ((uint64_t)inqbuf->atap_max_lba[2] << 32) | + ((uint64_t)inqbuf->atap_max_lba[1] << 16) | + ((uint64_t)inqbuf->atap_max_lba[0] << 0); + } else if (inqbuf->atap_capabilities1 & WDC_CAP_LBA) { + sectors = (inqbuf->atap_capacity[1] << 16) | + inqbuf->atap_capacity[0]; + } else { + sectors = inqbuf->atap_cylinders * + inqbuf->atap_heads * inqbuf->atap_sectors; + } + + secsize = 512; + + if ((inqbuf->atap_secsz & ATA_SECSZ_VALID_MASK) == ATA_SECSZ_VALID) { + if (inqbuf->atap_secsz & ATA_SECSZ_LLS) { + secsize = 2 * /* words to bytes */ + (inqbuf->atap_lls_secsz[1] << 16 | + inqbuf->atap_lls_secsz[0] << 0); + } + } + + capacity = sectors * secsize; + + if (capacityp) + *capacityp = capacity; + if (sectorsp) + *sectorsp = sectors; + if (secsizep) + *secsizep = secsize; +} + /* * DEVICE COMMANDS */ @@ -934,32 +980,7 @@ device_identify(int argc, char *argv[]) "ATAPI" : "ATA", inqbuf->atap_config & ATA_CFG_FIXED ? "fixed" : "removable"); - if (inqbuf->atap_cmd2_en != 0 && inqbuf->atap_cmd2_en != 0xffff && - inqbuf->atap_cmd2_en & ATA_CMD2_LBA48) { - sectors = - ((uint64_t)inqbuf->atap_max_lba[3] << 48) | - ((uint64_t)inqbuf->atap_max_lba[2] << 32) | - ((uint64_t)inqbuf->atap_max_lba[1] << 16) | - ((uint64_t)inqbuf->atap_max_lba[0] << 0); - } else if (inqbuf->atap_capabilities1 & WDC_CAP_LBA) { - sectors = (inqbuf->atap_capacity[1] << 16) | - inqbuf->atap_capacity[0]; - } else { - sectors = inqbuf->atap_cylinders * - inqbuf->atap_heads * inqbuf->atap_sectors; - } - - secsize = 512; - - if ((inqbuf->atap_secsz & ATA_SECSZ_VALID_MASK) == ATA_SECSZ_VALID) { - if (inqbuf->atap_secsz & ATA_SECSZ_LLS) { - secsize = 2 * /* words to bytes */ - (inqbuf->atap_lls_secsz[1] << 16 | - inqbuf->atap_lls_secsz[0] << 0); - } - } - - capacity = sectors * secsize; + compute_capacity(inqbuf, &capacity, §ors, &secsize); humanize_number(hnum, sizeof(hnum), capacity, "bytes", HN_AUTOSCALE, HN_DIVISOR_1000); @@ -1368,23 +1389,124 @@ device_security(int argc, char *argv[]) { struct atareq req; const struct ataparams *inqbuf; + unsigned char data[DEV_BSIZE]; + char *pass; /* need subcommand */ if (argc < 1) usage(); - if (strcmp(argv[0], "freeze") == 0) { - memset(&req, 0, sizeof(req)); + memset(&req, 0, sizeof(req)); + if (strcmp(argv[0], "status") == 0) { + inqbuf = getataparams(); + print_bitinfo("\t", "\n", inqbuf->atap_sec_st, ata_sec_st); + } else if (strcmp(argv[0], "freeze") == 0) { req.command = WDCC_SECURITY_FREEZE; req.timeout = 1000; ata_command(&req); - } else if (strcmp(argv[0], "status") == 0) { - inqbuf = getataparams(); - print_bitinfo("\t", "\n", inqbuf->atap_sec_st, ata_sec_st); - } else - usage(); + } else if ((strcmp(argv[0], "setpass") == 0) || + (strcmp(argv[0], "unlock") == 0) || + (strcmp(argv[0], "disable") == 0) || + (strcmp(argv[0], "erase") == 0)) { + if (argc != 2) + usage(); + if (strcmp(argv[1], "user") != 0) { + if (strcmp(argv[1], "master") == 0) { + fprintf(stderr, + "Master passwords not supported\n"); + exit(1); + } else { + usage(); + } + } - return; + pass = getpass("Password:"); + if (strlen(pass) > 32) { + fprintf(stderr, "Password must be <=32 characters\n"); + exit(1); + } + + req.flags |= ATACMD_WRITE; + req.timeout = 1000; + req.databuf = data; + req.datalen = sizeof(data); + memset(data, 0, sizeof(data)); + strlcpy((void *)&data[2], pass, 32 + 1); + + if (strcmp(argv[0], "setpass") == 0) { + char orig[32 + 1]; + strlcpy(orig, pass, 32 + 1); + pass = getpass("Confirm password:"); + if (0 != strcmp(orig, pass)) { + fprintf(stderr, "Passwords do not match\n"); + exit(1); + } + req.command = WDCC_SECURITY_SET_PASSWORD; + } else if (strcmp(argv[0], "unlock") == 0) { + req.command = WDCC_SECURITY_UNLOCK; + } else if (strcmp(argv[0], "disable") == 0) { + req.command = WDCC_SECURITY_DISABLE_PASSWORD; + } else if (strcmp(argv[0], "erase") == 0) { + struct atareq prepare; + + inqbuf = getataparams(); + + /* + * XXX Any way to lock the device to make sure + * this really is the command preceding the + * SECURITY ERASE UNIT command? This would + * probably have to be moved into the kernel to + * do that. + */ + memset(&prepare, 0, sizeof(prepare)); + prepare.command = WDCC_SECURITY_ERASE_PREPARE; + prepare.timeout = 1000; + ata_command(&prepare); + + req.command = WDCC_SECURITY_ERASE_UNIT; + + /* + * Enable enhanced erase if it's supported. + * + * XXX should be a command-line option + */ + if (inqbuf->atap_sec_st & WDC_SEC_ESE_SUPP) { + data[0] |= 0x2; + req.timeout = (inqbuf->atap_eseu_time & 0xff) + * 2 * 60 * 1000; + } else { + req.timeout = (inqbuf->atap_seu_time & 0xff) + * 2 * 60 * 1000; + } + + /* + * If the estimated time was 0xff (* 2 * 60 * + * 1000 = 30600000), that means `>508 minutes'. + * Estimate that we can handle 16 MB/sec, a + * rate I just pulled out of my arse. + */ + if (req.timeout == 30600000) { + uint64_t bytes, timeout; + compute_capacity(inqbuf, &bytes, NULL, NULL); + timeout = (bytes / (16 * 1024 * 1024)) * 1000; + if (timeout > (uint64_t)INT_MAX) + req.timeout = INT_MAX; + else + req.timeout = timeout; + } + + printf("Erasing may take up to %dh %dm %ds...\n", + (req.timeout / 1000 / 60) / 60, + (req.timeout / 1000 / 60) % 60, + req.timeout % 60); + } else { + abort(); + } + + ata_command(&req); + } else { + usage(); + } } /*