Adds Host Protected Area (HPA) support for ATA disks to camcontrol
Reviewed by: mav Approved by: pjd (mentor) MFC after: 2 weeks
This commit is contained in:
parent
4c7a605968
commit
9e68761ce0
@ -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 command will fail.
|
||||
.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 data before using!
|
||||
.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 ,
|
||||
|
@ -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_table[] = {
|
||||
{"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 *device, int argc, char **argv,
|
||||
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
|
||||
@ -1127,6 +1158,38 @@ camxferrate(struct cam_device *device)
|
||||
return(retval);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@ -1554,6 +1617,83 @@ ata_do_28bit_cmd(struct cam_device *device, union ccb *ccb, int retries,
|
||||
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)
|
||||
{
|
||||
@ -1570,6 +1710,278 @@ dump_data(uint16_t *ptr, uint32_t len)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
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, int retry_count, int timeout)
|
||||
{
|
||||
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, int retry_count, int timeout)
|
||||
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, char opt)
|
||||
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 <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)
|
||||
@ -6852,6 +7517,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 */
|
||||
}
|
||||
@ -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();
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user