Author: smh
Date: Fri Jun  7 14:23:11 2013
New Revision: 251498
URL: http://svnweb.freebsd.org/changeset/base/251498

Log:
  MFC r249895:
  Add Host Protected Area (HPA) support to camcontrol
  
  MFC r249904:
  mdoc: remove superfluous paragraph macro.

Modified:
  stable/9/sbin/camcontrol/camcontrol.8
  stable/9/sbin/camcontrol/camcontrol.c
  stable/9/sys/sys/ata.h
Directory Properties:
  stable/9/sbin/camcontrol/   (props changed)
  stable/9/sys/   (props changed)
  stable/9/sys/sys/   (props changed)

Modified: stable/9/sbin/camcontrol/camcontrol.8
==============================================================================
--- stable/9/sbin/camcontrol/camcontrol.8       Fri Jun  7 14:10:18 2013        
(r251497)
+++ stable/9/sbin/camcontrol/camcontrol.8       Fri Jun  7 14:23:11 2013        
(r251498)
@@ -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
@@ -1204,6 +1216,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
@@ -1396,6 +1475,28 @@ 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.
+.Bd -literal -offset indent
+camcontrol hpa ada0
+.Ed
+.Pp
+Report HPA support and settings for ada0 (also reported via
+identify).
+.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: stable/9/sbin/camcontrol/camcontrol.c
==============================================================================
--- stable/9/sbin/camcontrol/camcontrol.c       Fri Jun  7 14:10:18 2013        
(r251497)
+++ stable/9/sbin/camcontrol/camcontrol.c       Fri Jun  7 14:23:11 2013        
(r251498)
@@ -45,6 +45,10 @@ __FBSDID("$FreeBSD$");
 #include <ctype.h>
 #include <err.h>
 #include <libutil.h>
+#ifndef MINIMALISTIC
+#include <limits.h>
+#include <inttypes.h>
+#endif
 
 #include <cam/cam.h>
 #include <cam/cam_debug.h>
@@ -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)
@@ -6701,6 +7364,8 @@ usage(int printlong)
 "                              <-d pwd | -e pwd | -f | -h pwd | -k pwd>\n"
 "                              [-l <high|maximum>] [-q] [-s pwd] [-T 
timeout]\n"
 "                              [-U <user|master>] [-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)
@@ -6851,6 +7516,17 @@ usage(int printlong)
 "-T timeout        overrides the timeout (seconds) used for erase operation\n"
 "-U <user|master>  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 */
 }
@@ -7075,6 +7751,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: stable/9/sys/sys/ata.h
==============================================================================
--- stable/9/sys/sys/ata.h      Fri Jun  7 14:10:18 2013        (r251497)
+++ stable/9/sys/sys/ata.h      Fri Jun  7 14:23:11 2013        (r251498)
@@ -289,6 +289,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
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to