#include "nvme_common.h"
#include <sys/sysmacros.h>
#ifdef _KERNEL
#include <sys/sunddi.h>
#include <sys/stdint.h>
#else
#include <stdio.h>
#include <inttypes.h>
#endif
#define NVME_DEFAULT_FWUG 4096
#define NVME_FWUG_MULT 4096
bool
nvme_fw_cmds_supported(const nvme_valid_ctrl_data_t *data)
{
return (data->vcd_id->id_oacs.oa_firmware != 0);
}
uint32_t
nvme_fw_load_granularity(const nvme_valid_ctrl_data_t *data)
{
uint32_t gran = NVME_DEFAULT_FWUG;
if (nvme_vers_atleast(data->vcd_vers, &nvme_vers_1v3)) {
const uint8_t fwug = data->vcd_id->ap_fwug;
if (fwug == 0xff) {
gran = NVME_DWORD_SIZE;
} else if (fwug != 0) {
gran = fwug * NVME_FWUG_MULT;
}
}
return (gran);
}
static bool
nvme_fw_load_field_valid_len(const nvme_field_info_t *field,
const nvme_valid_ctrl_data_t *data, uint64_t len, char *msg, size_t msglen)
{
if ((len & NVME_DWORD_MASK) != 0) {
(void) snprintf(msg, msglen, "%s (%s) value 0x%" PRIx64 " must "
"be aligned to the firmware update granularity 0x%x",
field->nlfi_human, field->nlfi_spec, len, NVME_DWORD_SIZE);
return (false);
}
return (nvme_field_range_check(field, NVME_DWORD_SIZE,
NVME_FW_LENB_MAX, msg, msglen, len));
}
static bool
nvme_fw_load_field_valid_offset(const nvme_field_info_t *field,
const nvme_valid_ctrl_data_t *data, uint64_t off, char *msg, size_t msglen)
{
uint32_t gran = nvme_fw_load_granularity(data);
if ((off % gran) != 0) {
(void) snprintf(msg, msglen, "%s (%s) value 0x%" PRIx64 " must "
"be aligned to the firmware update granularity 0x%x",
field->nlfi_human, field->nlfi_spec, off, gran);
return (false);
}
return (nvme_field_range_check(field, 0, NVME_FW_OFFSETB_MAX, msg,
msglen, off));
}
const nvme_field_info_t nvme_fw_load_fields[] = {
[NVME_FW_LOAD_REQ_FIELD_NUMD] = {
.nlfi_vers = &nvme_vers_1v0,
.nlfi_valid = nvme_fw_load_field_valid_len,
.nlfi_spec = "numd",
.nlfi_human = "number of dwords",
.nlfi_def_req = true,
.nlfi_def_allow = true
},
[NVME_FW_LOAD_REQ_FIELD_OFFSET] = {
.nlfi_vers = &nvme_vers_1v0,
.nlfi_valid = nvme_fw_load_field_valid_offset,
.nlfi_spec = "ofst",
.nlfi_human = "offset",
.nlfi_def_req = true,
.nlfi_def_allow = true
}
};
const size_t nvme_fw_load_nfields = ARRAY_SIZE(nvme_fw_load_fields);
static bool
nvme_fw_commit_field_valid_slot(const nvme_field_info_t *field,
const nvme_valid_ctrl_data_t *data, uint64_t slot, char *msg, size_t msglen)
{
return (nvme_field_range_check(field, NVME_FW_SLOT_MIN,
data->vcd_id->id_frmw.fw_nslot, msg, msglen, slot));
}
static bool
nvme_fw_commit_field_valid_act(const nvme_field_info_t *field,
const nvme_valid_ctrl_data_t *data, uint64_t act, char *msg, size_t msglen)
{
uint64_t max = NVME_FWC_ACTIVATE;
if (nvme_vers_atleast(data->vcd_vers, &nvme_vers_1v3)) {
max = NVME_FWC_ACTIVATE_IMMED;
}
return (nvme_field_range_check(field, 0, max, msg, msglen, act));
}
const nvme_field_info_t nvme_fw_commit_fields[] = {
[NVME_FW_COMMIT_REQ_FIELD_SLOT] = {
.nlfi_vers = &nvme_vers_1v0,
.nlfi_valid = nvme_fw_commit_field_valid_slot,
.nlfi_spec = "fs",
.nlfi_human = "firmware slot",
.nlfi_def_req = true,
.nlfi_def_allow = true
},
[NVME_FW_COMMIT_REQ_FIELD_ACT] = {
.nlfi_vers = &nvme_vers_1v0,
.nlfi_valid = nvme_fw_commit_field_valid_act,
.nlfi_spec = "ca",
.nlfi_human = "commit action",
.nlfi_def_req = true,
.nlfi_def_allow = true
}
};
const size_t nvme_fw_commit_nfields = ARRAY_SIZE(nvme_fw_commit_fields);