From owner-svn-src-head@FreeBSD.ORG Thu Apr 25 14:11:39 2013 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by hub.freebsd.org (Postfix) with ESMTP id 21FAD675; Thu, 25 Apr 2013 14:11:39 +0000 (UTC) (envelope-from smh@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) by mx1.freebsd.org (Postfix) with ESMTP id 037C9171F; Thu, 25 Apr 2013 14:11:39 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.6/8.14.6) with ESMTP id r3PEBd19089574; Thu, 25 Apr 2013 14:11:39 GMT (envelope-from smh@svn.freebsd.org) Received: (from smh@localhost) by svn.freebsd.org (8.14.6/8.14.5/Submit) id r3PEBcvE089571; Thu, 25 Apr 2013 14:11:38 GMT (envelope-from smh@svn.freebsd.org) Message-Id: <201304251411.r3PEBcvE089571@svn.freebsd.org> From: Steven Hartland Date: Thu, 25 Apr 2013 14:11:38 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r249895 - in head: sbin/camcontrol sys/sys X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 25 Apr 2013 14:11:39 -0000 Author: smh Date: Thu Apr 25 14:11:38 2013 New Revision: 249895 URL: http://svnweb.freebsd.org/changeset/base/249895 Log: Adds Host Protected Area (HPA) support for ATA disks to camcontrol Reviewed by: mav Approved by: pjd (mentor) MFC after: 2 weeks Modified: head/sbin/camcontrol/camcontrol.8 head/sbin/camcontrol/camcontrol.c head/sys/sys/ata.h Modified: head/sbin/camcontrol/camcontrol.8 ============================================================================== --- head/sbin/camcontrol/camcontrol.8 Thu Apr 25 12:42:09 2013 (r249894) +++ head/sbin/camcontrol/camcontrol.8 Thu Apr 25 14:11:38 2013 (r249895) @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd June 4, 2012 +.Dd April 24, 2013 .Dt CAMCONTROL 8 .Os .Sh NAME @@ -243,6 +243,18 @@ .Op Fl U Ar user|master .Op Fl y .Nm +.Ic hpa +.Op device id +.Op generic args +.Op Fl f +.Op Fl l +.Op Fl P +.Op Fl p Ar pwd +.Op Fl q +.Op Fl s Ar max_sectors +.Op Fl U Ar pwd +.Op Fl y +.Nm .Ic help .Sh DESCRIPTION The @@ -1205,6 +1217,73 @@ password for the specified user the comm .Pp The password in all cases is limited to 32 characters, longer passwords will fail. +.It Ic hpa +Update or report Host Protected Area details. +By default +.Nm +will print out the HPA support and associated settings of the device. +The +.Ic hpa +command takes several optional arguments: +.Bl -tag -width 0n +.It Fl f +.Pp +Freeze the HPA configuration of the specified device. +.Pp +After command completion any other commands that update the HPA configuration +shall be command aborted. +Frozen mode is disabled by power-off or hardware reset. +.It Fl l +.Pp +Lock the HPA configuration of the device until a successful call to unlock or +the next power-on reset occurs. +.It Fl P +.Pp +Make the HPA max sectors persist across power-on reset or a hardware reset. +This must be used in combination with +.Fl s Ar max_sectors +. +.It Fl p Ar pwd +.Pp +Set the HPA configuration password required for unlock calls. +.It Fl q +.Pp +Be quiet, do not print any status messages. +This option will not disable the questions. +To disable questions, use the +.Fl y +argument, below. +.It Fl s Ar max_sectors +.Pp +Configures the maximum user accessible sectors of the device. +This will change the number of sectors the device reports. +.Pp +.Em WARNING! WARNING! WARNING! +.Pp +Changing the max sectors of a device using this option will make the data on +the device beyond the specified value inaccessible. +.Pp +Only one successful +.Fl s Ar max_sectors +call can be made without a power-on reset or a hardware reset of the device. +.It Fl U Ar pwd +.Pp +Unlock the HPA configuration of the specified device using the given password. +If the password specified doesn't match the password configured via +.Fl p Ar pwd +the command will fail. +.Pp +After 5 failed unlock calls, due to password miss-match, the device will refuse +additional unlock calls until after a power-on reset. +.It Fl y +.Pp +Confirm yes to dangerous options such as +.Fl e +without prompting for confirmation +.Pp +.El +The password for all HPA commands is limited to 32 characters, longer passwords +will fail. .It Ic fwdownload Program firmware of the named SCSI device using the image file provided. .Pp @@ -1397,6 +1476,30 @@ data from the device, so backup your dat .Pp This command can be used used against an SSD drive to restoring it to factory default write performance. +.Pp +.Bd -literal -offset indent +camcontrol hpa ada0 +.Ed +.Pp +Report HPA support and settings for ada0 (also reported via +identify). +.Pp +.Bd -literal -offset indent +camcontrol hpa ada0 -s 10240 +.Ed +.Pp +Enables HPA on ada0 setting the maximum reported sectors to 10240. +.Pp +.Em WARNING! WARNING! WARNING! +.Pp +This will +.Em PREVENT ACCESS +to all data on the device beyond this limit until HPA is disabled by setting +HPA to native max sectors of the device, which can only be done after a +power-on or hardware reset! +.Pp +.Em DO NOT +use this on a device which has an active filesystem! .Sh SEE ALSO .Xr cam 3 , .Xr cam_cdbparse 3 , Modified: head/sbin/camcontrol/camcontrol.c ============================================================================== --- head/sbin/camcontrol/camcontrol.c Thu Apr 25 12:42:09 2013 (r249894) +++ head/sbin/camcontrol/camcontrol.c Thu Apr 25 14:11:38 2013 (r249895) @@ -45,6 +45,10 @@ __FBSDID("$FreeBSD$"); #include #include #include +#ifndef MINIMALISTIC +#include +#include +#endif #include #include @@ -88,7 +92,8 @@ typedef enum { CAM_CMD_SMP_PHYLIST = 0x0000001a, CAM_CMD_SMP_MANINFO = 0x0000001b, CAM_CMD_DOWNLOAD_FW = 0x0000001c, - CAM_CMD_SECURITY = 0x0000001d + CAM_CMD_SECURITY = 0x0000001d, + CAM_CMD_HPA = 0x0000001e } cam_cmdmask; typedef enum { @@ -135,6 +140,29 @@ struct camcontrol_opts { }; #ifndef MINIMALISTIC +struct ata_res_pass16 { + u_int16_t reserved[5]; + u_int8_t flags; + u_int8_t error; + u_int8_t sector_count_exp; + u_int8_t sector_count; + u_int8_t lba_low_exp; + u_int8_t lba_low; + u_int8_t lba_mid_exp; + u_int8_t lba_mid; + u_int8_t lba_high_exp; + u_int8_t lba_high; + u_int8_t device; + u_int8_t status; +}; + +struct ata_set_max_pwd +{ + u_int16_t reserved1; + u_int8_t password[32]; + u_int16_t reserved2[239]; +}; + static const char scsicmd_opts[] = "a:c:dfi:o:r"; static const char readdefect_opts[] = "f:GP"; static const char negotiate_opts[] = "acD:M:O:qR:T:UW:"; @@ -186,6 +214,7 @@ static struct camcontrol_opts option_tab {"sleep", CAM_CMD_SLEEP, CAM_ARG_NONE, ""}, {"fwdownload", CAM_CMD_DOWNLOAD_FW, CAM_ARG_NONE, "f:ys"}, {"security", CAM_CMD_SECURITY, CAM_ARG_NONE, "d:e:fh:k:l:qs:T:U:y"}, + {"hpa", CAM_CMD_HPA, CAM_ARG_NONE, "Pflp:qs:U:y"}, #endif /* MINIMALISTIC */ {"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, {"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, @@ -280,6 +309,8 @@ static int atapm(struct cam_device *devi char *combinedopt, int retry_count, int timeout); static int atasecurity(struct cam_device *device, int retry_count, int timeout, int argc, char **argv, char *combinedopt); +static int atahpa(struct cam_device *device, int retry_count, int timeout, + int argc, char **argv, char *combinedopt); #endif /* MINIMALISTIC */ #ifndef min @@ -1128,6 +1159,38 @@ xferrate_bailout: } static void +atahpa_print(struct ata_params *parm, u_int64_t hpasize, int header) +{ + u_int32_t lbasize = (u_int32_t)parm->lba_size_1 | + ((u_int32_t)parm->lba_size_2 << 16); + + u_int64_t lbasize48 = ((u_int64_t)parm->lba_size48_1) | + ((u_int64_t)parm->lba_size48_2 << 16) | + ((u_int64_t)parm->lba_size48_3 << 32) | + ((u_int64_t)parm->lba_size48_4 << 48); + + if (header) { + printf("\nFeature " + "Support Enabled Value\n"); + } + + printf("Host Protected Area (HPA) "); + if (parm->support.command1 & ATA_SUPPORT_PROTECTED) { + u_int64_t lba = lbasize48 ? lbasize48 : lbasize; + printf("yes %s %ju/%ju\n", (hpasize > lba) ? "yes" : "no ", + lba, hpasize); + + printf("HPA - Security "); + if (parm->support.command1 & ATA_SUPPORT_MAXSECURITY) + printf("yes\n"); + else + printf("no\n"); + } else { + printf("no\n"); + } +} + +static void atacapprint(struct ata_params *parm) { u_int32_t lbasize = (u_int32_t)parm->lba_size_1 | @@ -1554,6 +1617,83 @@ ata_do_28bit_cmd(struct cam_device *devi return ata_cam_send(device, ccb, quiet); } +static int +ata_do_cmd(struct cam_device *device, union ccb *ccb, int retries, + u_int32_t flags, u_int8_t protocol, u_int8_t ata_flags, + u_int8_t tag_action, u_int8_t command, u_int8_t features, + u_int64_t lba, u_int8_t sector_count, u_int8_t *data_ptr, + u_int16_t dxfer_len, int timeout, int force48bit) +{ + int retval; + + retval = ata_try_pass_16(device); + if (retval == -1) + return (1); + + if (retval == 1) { + int error; + + /* Try using SCSI Passthrough */ + error = ata_do_pass_16(device, ccb, retries, flags, protocol, + ata_flags, tag_action, command, features, + lba, sector_count, data_ptr, dxfer_len, + timeout, 0); + + if (ata_flags & AP_FLAG_CHK_COND) { + /* Decode ata_res from sense data */ + struct ata_res_pass16 *res_pass16; + struct ata_res *res; + u_int i; + u_int16_t *ptr; + + /* sense_data is 4 byte aligned */ + ptr = (uint16_t*)(uintptr_t)&ccb->csio.sense_data; + for (i = 0; i < sizeof(*res_pass16) / 2; i++) + ptr[i] = le16toh(ptr[i]); + + /* sense_data is 4 byte aligned */ + res_pass16 = (struct ata_res_pass16 *)(uintptr_t) + &ccb->csio.sense_data; + res = &ccb->ataio.res; + res->flags = res_pass16->flags; + res->status = res_pass16->status; + res->error = res_pass16->error; + res->lba_low = res_pass16->lba_low; + res->lba_mid = res_pass16->lba_mid; + res->lba_high = res_pass16->lba_high; + res->device = res_pass16->device; + res->lba_low_exp = res_pass16->lba_low_exp; + res->lba_mid_exp = res_pass16->lba_mid_exp; + res->lba_high_exp = res_pass16->lba_high_exp; + res->sector_count = res_pass16->sector_count; + res->sector_count_exp = res_pass16->sector_count_exp; + } + + return (error); + } + + bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_ataio) - + sizeof(struct ccb_hdr)); + cam_fill_ataio(&ccb->ataio, + retries, + NULL, + flags, + tag_action, + data_ptr, + dxfer_len, + timeout); + + if (force48bit || lba > ATA_MAX_28BIT_LBA) + ata_48bit_cmd(&ccb->ataio, command, features, lba, sector_count); + else + ata_28bit_cmd(&ccb->ataio, command, features, lba, sector_count); + + if (ata_flags & AP_FLAG_CHK_COND) + ccb->ataio.cmd.flags |= CAM_ATAIO_NEEDRESULT; + + return ata_cam_send(device, ccb, 0); +} + static void dump_data(uint16_t *ptr, uint32_t len) { @@ -1571,6 +1711,278 @@ dump_data(uint16_t *ptr, uint32_t len) } static int +atahpa_proc_resp(struct cam_device *device, union ccb *ccb, + int is48bit, u_int64_t *hpasize) +{ + struct ata_res *res; + + res = &ccb->ataio.res; + if (res->status & ATA_STATUS_ERROR) { + if (arglist & CAM_ARG_VERBOSE) { + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + printf("error = 0x%02x, sector_count = 0x%04x, " + "device = 0x%02x, status = 0x%02x\n", + res->error, res->sector_count, + res->device, res->status); + } + + if (res->error & ATA_ERROR_ID_NOT_FOUND) { + warnx("Max address has already been set since " + "last power-on or hardware reset"); + } + + return (1); + } + + if (arglist & CAM_ARG_VERBOSE) { + fprintf(stdout, "%s%d: Raw native max data:\n", + device->device_name, device->dev_unit_num); + /* res is 4 byte aligned */ + dump_data((uint16_t*)(uintptr_t)res, sizeof(struct ata_res)); + + printf("error = 0x%02x, sector_count = 0x%04x, device = 0x%02x, " + "status = 0x%02x\n", res->error, res->sector_count, + res->device, res->status); + } + + if (hpasize != NULL) { + if (is48bit) { + *hpasize = (((u_int64_t)((res->lba_high_exp << 16) | + (res->lba_mid_exp << 8) | res->lba_low_exp) << 24) | + ((res->lba_high << 16) | (res->lba_mid << 8) | + res->lba_low)) + 1; + } else { + *hpasize = (((res->device & 0x0f) << 24) | + (res->lba_high << 16) | (res->lba_mid << 8) | + res->lba_low) + 1; + } + } + + return (0); +} + +static int +ata_read_native_max(struct cam_device *device, int retry_count, + u_int32_t timeout, union ccb *ccb, + struct ata_params *parm, u_int64_t *hpasize) +{ + int error; + u_int cmd, is48bit; + u_int8_t protocol; + + is48bit = parm->support.command2 & ATA_SUPPORT_ADDRESS48; + protocol = AP_PROTO_NON_DATA; + + if (is48bit) { + cmd = ATA_READ_NATIVE_MAX_ADDRESS48; + protocol |= AP_EXTEND; + } else { + cmd = ATA_READ_NATIVE_MAX_ADDRESS; + } + + error = ata_do_cmd(device, + ccb, + retry_count, + /*flags*/CAM_DIR_IN, + /*protocol*/protocol, + /*ata_flags*/AP_FLAG_CHK_COND, + /*tag_action*/MSG_SIMPLE_Q_TAG, + /*command*/cmd, + /*features*/0, + /*lba*/0, + /*sector_count*/0, + /*data_ptr*/NULL, + /*dxfer_len*/0, + timeout ? timeout : 1000, + is48bit); + + if (error) + return (error); + + return atahpa_proc_resp(device, ccb, is48bit, hpasize); +} + +static int +atahpa_set_max(struct cam_device *device, int retry_count, + u_int32_t timeout, union ccb *ccb, + int is48bit, u_int64_t maxsize, int persist) +{ + int error; + u_int cmd; + u_int8_t protocol; + + protocol = AP_PROTO_NON_DATA; + + if (is48bit) { + cmd = ATA_SET_MAX_ADDRESS48; + protocol |= AP_EXTEND; + } else { + cmd = ATA_SET_MAX_ADDRESS; + } + + /* lba's are zero indexed so the max lba is requested max - 1 */ + if (maxsize) + maxsize--; + + error = ata_do_cmd(device, + ccb, + retry_count, + /*flags*/CAM_DIR_OUT, + /*protocol*/protocol, + /*ata_flags*/AP_FLAG_CHK_COND, + /*tag_action*/MSG_SIMPLE_Q_TAG, + /*command*/cmd, + /*features*/ATA_HPA_FEAT_MAX_ADDR, + /*lba*/maxsize, + /*sector_count*/persist, + /*data_ptr*/NULL, + /*dxfer_len*/0, + timeout ? timeout : 1000, + is48bit); + + if (error) + return (error); + + return atahpa_proc_resp(device, ccb, is48bit, NULL); +} + +static int +atahpa_password(struct cam_device *device, int retry_count, + u_int32_t timeout, union ccb *ccb, + int is48bit, struct ata_set_max_pwd *pwd) +{ + int error; + u_int cmd; + u_int8_t protocol; + + protocol = AP_PROTO_PIO_OUT; + cmd = (is48bit) ? ATA_SET_MAX_ADDRESS48 : ATA_SET_MAX_ADDRESS; + + error = ata_do_cmd(device, + ccb, + retry_count, + /*flags*/CAM_DIR_OUT, + /*protocol*/protocol, + /*ata_flags*/AP_FLAG_CHK_COND, + /*tag_action*/MSG_SIMPLE_Q_TAG, + /*command*/cmd, + /*features*/ATA_HPA_FEAT_SET_PWD, + /*lba*/0, + /*sector_count*/0, + /*data_ptr*/(u_int8_t*)pwd, + /*dxfer_len*/sizeof(struct ata_set_max_pwd), + timeout ? timeout : 1000, + is48bit); + + if (error) + return (error); + + return atahpa_proc_resp(device, ccb, is48bit, NULL); +} + +static int +atahpa_lock(struct cam_device *device, int retry_count, + u_int32_t timeout, union ccb *ccb, int is48bit) +{ + int error; + u_int cmd; + u_int8_t protocol; + + protocol = AP_PROTO_NON_DATA; + cmd = (is48bit) ? ATA_SET_MAX_ADDRESS48 : ATA_SET_MAX_ADDRESS; + + error = ata_do_cmd(device, + ccb, + retry_count, + /*flags*/CAM_DIR_OUT, + /*protocol*/protocol, + /*ata_flags*/AP_FLAG_CHK_COND, + /*tag_action*/MSG_SIMPLE_Q_TAG, + /*command*/cmd, + /*features*/ATA_HPA_FEAT_LOCK, + /*lba*/0, + /*sector_count*/0, + /*data_ptr*/NULL, + /*dxfer_len*/0, + timeout ? timeout : 1000, + is48bit); + + if (error) + return (error); + + return atahpa_proc_resp(device, ccb, is48bit, NULL); +} + +static int +atahpa_unlock(struct cam_device *device, int retry_count, + u_int32_t timeout, union ccb *ccb, + int is48bit, struct ata_set_max_pwd *pwd) +{ + int error; + u_int cmd; + u_int8_t protocol; + + protocol = AP_PROTO_PIO_OUT; + cmd = (is48bit) ? ATA_SET_MAX_ADDRESS48 : ATA_SET_MAX_ADDRESS; + + error = ata_do_cmd(device, + ccb, + retry_count, + /*flags*/CAM_DIR_OUT, + /*protocol*/protocol, + /*ata_flags*/AP_FLAG_CHK_COND, + /*tag_action*/MSG_SIMPLE_Q_TAG, + /*command*/cmd, + /*features*/ATA_HPA_FEAT_UNLOCK, + /*lba*/0, + /*sector_count*/0, + /*data_ptr*/(u_int8_t*)pwd, + /*dxfer_len*/sizeof(struct ata_set_max_pwd), + timeout ? timeout : 1000, + is48bit); + + if (error) + return (error); + + return atahpa_proc_resp(device, ccb, is48bit, NULL); +} + +static int +atahpa_freeze_lock(struct cam_device *device, int retry_count, + u_int32_t timeout, union ccb *ccb, int is48bit) +{ + int error; + u_int cmd; + u_int8_t protocol; + + protocol = AP_PROTO_NON_DATA; + cmd = (is48bit) ? ATA_SET_MAX_ADDRESS48 : ATA_SET_MAX_ADDRESS; + + error = ata_do_cmd(device, + ccb, + retry_count, + /*flags*/CAM_DIR_OUT, + /*protocol*/protocol, + /*ata_flags*/AP_FLAG_CHK_COND, + /*tag_action*/MSG_SIMPLE_Q_TAG, + /*command*/cmd, + /*features*/ATA_HPA_FEAT_FREEZE, + /*lba*/0, + /*sector_count*/0, + /*data_ptr*/NULL, + /*dxfer_len*/0, + timeout ? timeout : 1000, + is48bit); + + if (error) + return (error); + + return atahpa_proc_resp(device, ccb, is48bit, NULL); +} + + +static int ata_do_identify(struct cam_device *device, int retry_count, int timeout, union ccb *ccb, struct ata_params** ident_bufp) { @@ -1701,6 +2113,7 @@ ataidentify(struct cam_device *device, i { union ccb *ccb; struct ata_params *ident_buf; + u_int64_t hpasize; if ((ccb = cam_getccb(device)) == NULL) { warnx("couldn't allocate CCB"); @@ -1712,10 +2125,21 @@ ataidentify(struct cam_device *device, i return (1); } + if (ident_buf->support.command1 & ATA_SUPPORT_PROTECTED) { + if (ata_read_native_max(device, retry_count, timeout, ccb, + ident_buf, &hpasize) != 0) { + cam_freeccb(ccb); + return (1); + } + } else { + hpasize = 0; + } + printf("%s%d: ", device->device_name, device->dev_unit_num); ata_print_ident(ident_buf); camxferrate(device); atacapprint(ident_buf); + atahpa_print(ident_buf, hpasize, 0); free(ident_buf); cam_freeccb(ccb); @@ -2044,6 +2468,245 @@ ata_getpwd(u_int8_t *passwd, int max, ch return (0); } +enum { + ATA_HPA_ACTION_PRINT, + ATA_HPA_ACTION_SET_MAX, + ATA_HPA_ACTION_SET_PWD, + ATA_HPA_ACTION_LOCK, + ATA_HPA_ACTION_UNLOCK, + ATA_HPA_ACTION_FREEZE_LOCK +}; + +static int +atahpa_set_confirm(struct cam_device *device, struct ata_params* ident_buf, + u_int64_t maxsize, int persist) +{ + printf("\nYou are about to configure HPA to limit the user accessible\n" + "sectors to %ju %s on the device:\n%s%d,%s%d: ", maxsize, + persist ? "persistently" : "temporarily", + device->device_name, device->dev_unit_num, + device->given_dev_name, device->given_unit_number); + ata_print_ident(ident_buf); + + for(;;) { + char str[50]; + printf("\nAre you SURE you want to configure HPA? (yes/no) "); + + if (NULL != fgets(str, sizeof(str), stdin)) { + if (0 == strncasecmp(str, "yes", 3)) { + return (1); + } else if (0 == strncasecmp(str, "no", 2)) { + return (0); + } else { + printf("Please answer \"yes\" or " + "\"no\"\n"); + } + } + } + + /* NOTREACHED */ + return (0); +} + +static int +atahpa(struct cam_device *device, int retry_count, int timeout, + int argc, char **argv, char *combinedopt) +{ + union ccb *ccb; + struct ata_params *ident_buf; + struct ccb_getdev cgd; + struct ata_set_max_pwd pwd; + int error, confirm, quiet, c, action, actions, setpwd, persist; + int security, is48bit, pwdsize; + u_int64_t hpasize, maxsize; + + actions = 0; + setpwd = 0; + confirm = 0; + quiet = 0; + maxsize = 0; + persist = 0; + security = 0; + + memset(&pwd, 0, sizeof(pwd)); + + /* default action is to print hpa information */ + action = ATA_HPA_ACTION_PRINT; + pwdsize = sizeof(pwd.password); + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch(c){ + case 's': + action = ATA_HPA_ACTION_SET_MAX; + maxsize = strtoumax(optarg, NULL, 0); + actions++; + break; + + case 'p': + if (ata_getpwd(pwd.password, pwdsize, c) != 0) + return (1); + action = ATA_HPA_ACTION_SET_PWD; + security = 1; + actions++; + break; + + case 'l': + action = ATA_HPA_ACTION_LOCK; + security = 1; + actions++; + break; + + case 'U': + if (ata_getpwd(pwd.password, pwdsize, c) != 0) + return (1); + action = ATA_HPA_ACTION_UNLOCK; + security = 1; + actions++; + break; + + case 'f': + action = ATA_HPA_ACTION_FREEZE_LOCK; + security = 1; + actions++; + break; + + case 'P': + persist = 1; + break; + + case 'y': + confirm++; + break; + + case 'q': + quiet++; + break; + } + } + + if (actions > 1) { + warnx("too many hpa actions specified"); + return (1); + } + + if (get_cgd(device, &cgd) != 0) { + warnx("couldn't get CGD"); + return (1); + } + + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("couldn't allocate CCB"); + return (1); + } + + error = ata_do_identify(device, retry_count, timeout, ccb, &ident_buf); + if (error != 0) { + cam_freeccb(ccb); + return (1); + } + + if (quiet == 0) { + printf("%s%d: ", device->device_name, device->dev_unit_num); + ata_print_ident(ident_buf); + camxferrate(device); + } + + if (action == ATA_HPA_ACTION_PRINT) { + error = ata_read_native_max(device, retry_count, timeout, ccb, + ident_buf, &hpasize); + if (error == 0) + atahpa_print(ident_buf, hpasize, 1); + + cam_freeccb(ccb); + free(ident_buf); + return (error); + } + + if (!(ident_buf->support.command1 & ATA_SUPPORT_PROTECTED)) { + warnx("HPA is not supported by this device"); + cam_freeccb(ccb); + free(ident_buf); + return (1); + } + + if (security && !(ident_buf->support.command1 & ATA_SUPPORT_MAXSECURITY)) { + warnx("HPA Security is not supported by this device"); + cam_freeccb(ccb); + free(ident_buf); + return (1); + } + + is48bit = ident_buf->support.command2 & ATA_SUPPORT_ADDRESS48; + + /* + * The ATA spec requires: + * 1. Read native max addr is called directly before set max addr + * 2. Read native max addr is NOT called before any other set max call + */ + switch(action) { + case ATA_HPA_ACTION_SET_MAX: + if (confirm == 0 && + atahpa_set_confirm(device, ident_buf, maxsize, + persist) == 0) { + cam_freeccb(ccb); + free(ident_buf); + return (1); + } + + error = ata_read_native_max(device, retry_count, timeout, + ccb, ident_buf, &hpasize); + if (error == 0) { + error = atahpa_set_max(device, retry_count, timeout, + ccb, is48bit, maxsize, persist); + if (error == 0) { + /* redo identify to get new lba values */ + error = ata_do_identify(device, retry_count, + timeout, ccb, + &ident_buf); + atahpa_print(ident_buf, hpasize, 1); + } + } + break; + + case ATA_HPA_ACTION_SET_PWD: + error = atahpa_password(device, retry_count, timeout, + ccb, is48bit, &pwd); + if (error == 0) + printf("HPA password has been set\n"); + break; + + case ATA_HPA_ACTION_LOCK: + error = atahpa_lock(device, retry_count, timeout, + ccb, is48bit); + if (error == 0) + printf("HPA has been locked\n"); + break; + + case ATA_HPA_ACTION_UNLOCK: + error = atahpa_unlock(device, retry_count, timeout, + ccb, is48bit, &pwd); + if (error == 0) + printf("HPA has been unlocked\n"); + break; + + case ATA_HPA_ACTION_FREEZE_LOCK: + error = atahpa_freeze_lock(device, retry_count, timeout, + ccb, is48bit); + if (error == 0) + printf("HPA has been frozen\n"); + break; + + default: + errx(1, "Option currently not supported"); + } + + cam_freeccb(ccb); + free(ident_buf); + + return (error); +} + static int atasecurity(struct cam_device *device, int retry_count, int timeout, int argc, char **argv, char *combinedopt) @@ -6702,6 +7365,8 @@ usage(int printlong) " <-d pwd | -e pwd | -f | -h pwd | -k pwd>\n" " [-l ] [-q] [-s pwd] [-T timeout]\n" " [-U ] [-y]\n" +" camcontrol hpa [dev_id][generic args] [-f] [-l] [-P] [-p pwd]\n" +" [-q] [-s max_sectors] [-U pwd] [-y]\n" #endif /* MINIMALISTIC */ " camcontrol help\n"); if (!printlong) @@ -6852,6 +7517,17 @@ usage(int printlong) "-T timeout overrides the timeout (seconds) used for erase operation\n" "-U specifies which user to set: user or master\n" "-y don't ask any questions\n" +"hpa arguments:\n" +"-f freeze the HPA configuration of the device\n" +"-l lock the HPA configuration of the device\n" +"-P make the HPA max sectors persist\n" +"-p pwd Set the HPA configuration password required for unlock\n" +" calls\n" +"-q be quiet, do not print any status messages\n" +"-s sectors configures the maximum user accessible sectors of the\n" +" device\n" +"-U pwd unlock the HPA configuration of the device\n" +"-y don't ask any questions\n" ); #endif /* MINIMALISTIC */ } @@ -7076,6 +7752,10 @@ main(int argc, char **argv) case CAM_CMD_DEVLIST: error = getdevlist(cam_dev); break; + case CAM_CMD_HPA: + error = atahpa(cam_dev, retry_count, timeout, + argc, argv, combinedopt); + break; #endif /* MINIMALISTIC */ case CAM_CMD_DEVTREE: error = getdevtree(); Modified: head/sys/sys/ata.h ============================================================================== --- head/sys/sys/ata.h Thu Apr 25 12:42:09 2013 (r249894) +++ head/sys/sys/ata.h Thu Apr 25 14:11:38 2013 (r249895) @@ -283,6 +283,23 @@ struct ata_params { #define ATA_DEV_SLAVE 0x10 #define ATA_DEV_LBA 0x40 +/* ATA limits */ +#define ATA_MAX_28BIT_LBA 268435455UL + +/* ATA Status Register */ +#define ATA_STATUS_ERROR 0x01 +#define ATA_STATUS_DEVICE_FAULT 0x20 + +/* ATA Error Register */ +#define ATA_ERROR_ABORT 0x04 +#define ATA_ERROR_ID_NOT_FOUND 0x10 + +/* ATA HPA Features */ +#define ATA_HPA_FEAT_MAX_ADDR 0x00 +#define ATA_HPA_FEAT_SET_PWD 0x01 +#define ATA_HPA_FEAT_LOCK 0x02 +#define ATA_HPA_FEAT_UNLOCK 0x03 +#define ATA_HPA_FEAT_FREEZE 0x04 /* ATA transfer modes */ #define ATA_MODE_MASK 0x0f