blob: 131b740bfce62284ac3f6987282f4b40081c154d [file] [edit]
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright OpenBMC Authors
#include "fpga-controller.h"
#include "base.h"
#include <string.h>
/* Byte offsets inside a record, by record layout */
enum record_layout {
/* header, control, byte and transaction records */
REC_PROFILE = 0,
REC_OPCODE = 4,
REC_STATION = 5,
REC_MASK = 6,
REC_IDENTITY = 8,
REC_ARGUMENT = 28,
/* compact word record */
COMPACT_IDENTITY = 0,
COMPACT_STATION = 20,
COMPACT_OPCODE = 21,
COMPACT_MASK = 22,
COMPACT_VERSION = 23,
COMPACT_ADDRESS = 24,
COMPACT_DATA = 28,
/* control completion */
CTRL_RESULT = 5,
CTRL_OWNER = 6,
CTRL_STATE = 7,
/* word completion */
WORD_RESULT = 4,
WORD_FLAGS = 5,
WORD_OWNER = 6,
WORD_OPCODE = 7,
WORD_DATA = 28,
WORD_FAULT_RESPONSE = 28,
WORD_FAULT_REASON = 29,
/* authority status record */
STATUS_VERSION = 4,
STATUS_FLAGS = 5,
STATUS_OWNER = 6,
STATUS_RESERVED = 7,
STATUS_EPOCH = 8,
STATUS_SESSION = 16,
STATUS_GENERATION = 20,
STATUS_ZERO = 24,
};
/* Control completion state byte bits */
#define CTRL_STATE_OCCUPIED 0x01u
#define CTRL_STATE_PENDING 0x02u
#define CTRL_STATE_KNOWN (CTRL_STATE_OCCUPIED | CTRL_STATE_PENDING)
/* Bit 7 of a flags byte is reserved and reads zero */
#define FLAGS_RESERVED 0x80u
#define STATUS_RECORD_VERSION 1
#define PROFILE_BYTES sizeof(uint32_t)
static void put_le(uint8_t *record, size_t offset, uint64_t value, size_t bytes)
{
for (size_t i = 0; i < bytes; ++i) {
record[offset + i] = (uint8_t)(value >> (8 * i));
}
}
static uint64_t get_le(const uint8_t *record, size_t offset, size_t bytes)
{
uint64_t value = 0;
for (size_t i = 0; i < bytes; ++i) {
value |= (uint64_t)record[offset + i] << (8 * i);
}
return value;
}
bool nsm_fpga_identity_valid(const struct nsm_fpga_identity *identity)
{
return identity != NULL && identity->epoch != 0 &&
identity->session != 0 && identity->generation != 0 &&
identity->sequence != 0 &&
identity->sequence <= NSM_FPGA_MAX_SEQUENCE;
}
static void put_identity(uint8_t *record, size_t offset,
const struct nsm_fpga_identity *identity)
{
put_le(record, offset, identity->epoch, sizeof(identity->epoch));
offset += sizeof(identity->epoch);
put_le(record, offset, identity->session, sizeof(identity->session));
offset += sizeof(identity->session);
put_le(record, offset, identity->generation,
sizeof(identity->generation));
offset += sizeof(identity->generation);
put_le(record, offset, identity->sequence, sizeof(identity->sequence));
}
static void get_identity(const uint8_t *record, size_t offset,
struct nsm_fpga_identity *identity)
{
identity->epoch = get_le(record, offset, sizeof(identity->epoch));
offset += sizeof(identity->epoch);
identity->session =
(uint32_t)get_le(record, offset, sizeof(identity->session));
offset += sizeof(identity->session);
identity->generation =
(uint32_t)get_le(record, offset, sizeof(identity->generation));
offset += sizeof(identity->generation);
identity->sequence =
(uint32_t)get_le(record, offset, sizeof(identity->sequence));
}
static bool same_identity(const struct nsm_fpga_identity *a,
const struct nsm_fpga_identity *b)
{
return a->epoch == b->epoch && a->session == b->session &&
a->generation == b->generation && a->sequence == b->sequence;
}
static bool control_request_valid(const struct nsm_fpga_identity *identity,
uint8_t opcode, uint32_t argument)
{
if (identity == NULL) {
return false;
}
if (opcode == NSM_FPGA_CTRL_ACTIVATE ||
opcode == NSM_FPGA_CTRL_RECOVER) {
/* an authority request carries the epoch and nothing else */
return identity->epoch != 0 && identity->session == 0 &&
identity->generation == 0 && identity->sequence == 0 &&
argument == 0;
}
if (!nsm_fpga_identity_valid(identity)) {
return false;
}
switch (opcode) {
case NSM_FPGA_CTRL_ACQUIRE:
return identity->sequence == 1 && argument == 0;
case NSM_FPGA_CTRL_RENEW:
return identity->sequence > 1 && argument == 0;
case NSM_FPGA_CTRL_RELEASE:
return identity->sequence > 1 &&
argument <= NSM_FPGA_MAX_SEQUENCE;
default:
return false;
}
}
int nsm_fpga_encode_control(const struct nsm_fpga_identity *identity,
uint8_t opcode, uint32_t argument, uint8_t *record)
{
if (record == NULL) {
return NSM_SW_ERROR_NULL;
}
if (!control_request_valid(identity, opcode, argument)) {
return NSM_SW_ERROR_DATA;
}
memset(record, 0, NSM_FPGA_RECORD_SIZE);
put_le(record, REC_PROFILE, NSM_FPGA_PROFILE_IBC2, PROFILE_BYTES);
record[REC_OPCODE] = opcode;
put_identity(record, REC_IDENTITY, identity);
put_le(record, REC_ARGUMENT, argument, sizeof(argument));
return NSM_SW_SUCCESS;
}
int nsm_fpga_decode_control_completion(
const uint8_t *record, struct nsm_fpga_control_completion *completion)
{
if (record == NULL || completion == NULL) {
return NSM_SW_ERROR_NULL;
}
if (get_le(record, REC_PROFILE, PROFILE_BYTES) !=
NSM_FPGA_PROFILE_IBC2 ||
record[CTRL_RESULT] > NSM_FPGA_CTRL_EXHAUSTED ||
(record[CTRL_STATE] & ~CTRL_STATE_KNOWN) != 0) {
return NSM_SW_ERROR_DATA;
}
memset(completion, 0, sizeof(*completion));
completion->opcode = record[REC_OPCODE];
completion->result = record[CTRL_RESULT];
completion->owner = record[CTRL_OWNER];
completion->occupied = (record[CTRL_STATE] & CTRL_STATE_OCCUPIED) != 0;
completion->pending = (record[CTRL_STATE] & CTRL_STATE_PENDING) != 0;
get_identity(record, REC_IDENTITY, &completion->identity);
completion->argument = (uint32_t)get_le(record, REC_ARGUMENT,
sizeof(completion->argument));
if (!completion->occupied) {
/* an empty record carries nothing after the profile */
for (size_t i = PROFILE_BYTES; i < NSM_FPGA_RECORD_SIZE; ++i) {
if (record[i] != 0) {
return NSM_SW_ERROR_DATA;
}
}
return NSM_SW_SUCCESS;
}
/* an authority receipt (ACTIVATE, RECOVER) may name no owner */
const bool authority = completion->opcode == NSM_FPGA_CTRL_ACTIVATE ||
completion->opcode == NSM_FPGA_CTRL_RECOVER;
if ((completion->owner != NSM_FPGA_OWNER_NATIVE &&
completion->owner != NSM_FPGA_OWNER_NSM &&
!(authority && completion->owner == NSM_FPGA_OWNER_NONE)) ||
(!authority && completion->opcode != NSM_FPGA_CTRL_ACQUIRE &&
completion->opcode != NSM_FPGA_CTRL_RENEW &&
completion->opcode != NSM_FPGA_CTRL_RELEASE) ||
(completion->pending && completion->result != NSM_FPGA_CTRL_OK)) {
return NSM_SW_ERROR_DATA;
}
return NSM_SW_SUCCESS;
}
enum nsm_fpga_match
nsm_fpga_match_control(const struct nsm_fpga_control_completion *completion,
const struct nsm_fpga_identity *identity, uint8_t opcode,
uint32_t argument)
{
if (completion == NULL || identity == NULL) {
return NSM_FPGA_MATCH_REJECTED;
}
if (!completion->occupied) {
return NSM_FPGA_MATCH_EMPTY;
}
if (completion->owner != NSM_FPGA_OWNER_NSM ||
completion->opcode != opcode || completion->argument != argument ||
!same_identity(&completion->identity, identity)) {
return NSM_FPGA_MATCH_FOREIGN;
}
if (completion->pending) {
return NSM_FPGA_MATCH_PENDING;
}
return completion->result == NSM_FPGA_CTRL_OK ? NSM_FPGA_MATCH_SUCCESS
: NSM_FPGA_MATCH_REJECTED;
}
int nsm_fpga_decode_word_completion(const uint8_t *record,
struct nsm_fpga_word_completion *completion)
{
if (record == NULL || completion == NULL) {
return NSM_SW_ERROR_NULL;
}
if (get_le(record, REC_PROFILE, PROFILE_BYTES) !=
NSM_FPGA_PROFILE_IBC2 ||
record[WORD_RESULT] > NSM_FPGA_RESULT_FAULT ||
(record[WORD_FLAGS] & FLAGS_RESERVED) != 0) {
return NSM_SW_ERROR_DATA;
}
memset(completion, 0, sizeof(*completion));
completion->result = record[WORD_RESULT];
completion->flags = record[WORD_FLAGS];
completion->owner = record[WORD_OWNER];
completion->opcode = record[WORD_OPCODE];
get_identity(record, REC_IDENTITY, &completion->identity);
completion->data =
(uint32_t)get_le(record, WORD_DATA, sizeof(completion->data));
completion->fault_response = record[WORD_FAULT_RESPONSE];
completion->fault_reason = record[WORD_FAULT_REASON];
if (completion->result == NSM_FPGA_RESULT_EMPTY) {
/* an empty record carries nothing after the profile */
for (size_t i = PROFILE_BYTES; i < NSM_FPGA_RECORD_SIZE; ++i) {
if (record[i] != 0) {
return NSM_SW_ERROR_DATA;
}
}
return NSM_SW_SUCCESS;
}
if (!nsm_fpga_identity_valid(&completion->identity) ||
(completion->owner != NSM_FPGA_OWNER_NATIVE &&
completion->owner != NSM_FPGA_OWNER_NSM) ||
(completion->opcode != NSM_FPGA_OP_READ32 &&
completion->opcode != NSM_FPGA_OP_WRITE32 &&
completion->opcode != NSM_FPGA_OP_BYTE &&
!nsm_fpga_xfer_opcode_valid(completion->opcode)) ||
(completion->flags & NSM_FPGA_FLAG_OCCUPIED) == 0) {
return NSM_SW_ERROR_DATA;
}
return NSM_SW_SUCCESS;
}
enum nsm_fpga_match
nsm_fpga_match_word(const struct nsm_fpga_word_completion *completion,
const struct nsm_fpga_identity *identity, uint8_t opcode)
{
if (completion == NULL || identity == NULL) {
return NSM_FPGA_MATCH_REJECTED;
}
if (completion->result == NSM_FPGA_RESULT_EMPTY) {
return NSM_FPGA_MATCH_EMPTY;
}
if (completion->owner != NSM_FPGA_OWNER_NSM ||
completion->opcode != opcode ||
!same_identity(&completion->identity, identity)) {
return NSM_FPGA_MATCH_FOREIGN;
}
const uint8_t flags = completion->flags;
if ((flags & (NSM_FPGA_FLAG_BUS_HELD | NSM_FPGA_FLAG_BUS_RELEASED)) ==
(NSM_FPGA_FLAG_BUS_HELD | NSM_FPGA_FLAG_BUS_RELEASED) ||
((flags & NSM_FPGA_FLAG_PENDING) != 0 &&
(flags & (NSM_FPGA_FLAG_QUIESCENT | NSM_FPGA_FLAG_READ_VALID)) !=
0)) {
return NSM_FPGA_MATCH_REJECTED;
}
switch (completion->result) {
case NSM_FPGA_RESULT_PENDING:
if ((flags & (NSM_FPGA_FLAG_PENDING | NSM_FPGA_FLAG_FAULTED)) ==
NSM_FPGA_FLAG_PENDING &&
completion->data == 0) {
return NSM_FPGA_MATCH_PENDING;
}
return NSM_FPGA_MATCH_REJECTED;
case NSM_FPGA_RESULT_SUCCESS: {
const bool reading = opcode == NSM_FPGA_OP_READ32;
const bool read_valid = (flags & NSM_FPGA_FLAG_READ_VALID) != 0;
if ((flags & (NSM_FPGA_FLAG_PENDING | NSM_FPGA_FLAG_QUIESCENT |
NSM_FPGA_FLAG_FAULTED)) ==
NSM_FPGA_FLAG_QUIESCENT &&
read_valid == reading &&
(reading || completion->data == 0)) {
return NSM_FPGA_MATCH_SUCCESS;
}
return NSM_FPGA_MATCH_REJECTED;
}
case NSM_FPGA_RESULT_FAULT:
if ((flags &
(NSM_FPGA_FLAG_FAULTED | NSM_FPGA_FLAG_READ_VALID)) ==
NSM_FPGA_FLAG_FAULTED &&
(completion->data >> 16) == 0 &&
completion->fault_reason >= 1 &&
completion->fault_reason <= 8) {
return NSM_FPGA_MATCH_FAULT;
}
return NSM_FPGA_MATCH_REJECTED;
default:
return NSM_FPGA_MATCH_REJECTED;
}
}
int nsm_fpga_decode_authority_status(const uint8_t *record,
struct nsm_fpga_authority_status *status)
{
if (record == NULL || status == NULL) {
return NSM_SW_ERROR_NULL;
}
/* the reserved byte and the last eight bytes read zero */
if (get_le(record, REC_PROFILE, PROFILE_BYTES) !=
NSM_FPGA_PROFILE_IBC2 ||
record[STATUS_VERSION] != STATUS_RECORD_VERSION ||
(record[STATUS_FLAGS] & FLAGS_RESERVED) != 0 ||
record[STATUS_OWNER] > NSM_FPGA_OWNER_NSM ||
record[STATUS_RESERVED] != 0 ||
get_le(record, STATUS_ZERO, sizeof(uint64_t)) != 0) {
return NSM_SW_ERROR_DATA;
}
memset(status, 0, sizeof(*status));
status->flags = record[STATUS_FLAGS];
status->owner = record[STATUS_OWNER];
status->epoch = get_le(record, STATUS_EPOCH, sizeof(status->epoch));
status->session =
(uint32_t)get_le(record, STATUS_SESSION, sizeof(status->session));
status->generation = (uint32_t)get_le(record, STATUS_GENERATION,
sizeof(status->generation));
const bool initialized =
(status->flags & NSM_FPGA_AUTH_INITIALIZED) != 0;
if (initialized != (status->epoch != 0)) {
return NSM_SW_ERROR_DATA;
}
return NSM_SW_SUCCESS;
}
bool nsm_fpga_authority_acquirable(
const struct nsm_fpga_authority_status *status)
{
if (status == NULL) {
return false;
}
const uint8_t f = status->flags;
return (f & NSM_FPGA_AUTH_INITIALIZED) != 0 &&
(f & NSM_FPGA_AUTH_AVAILABLE) != 0 &&
(f & NSM_FPGA_AUTH_QUARANTINED) == 0 && status->epoch != 0;
}
bool nsm_fpga_authority_recoverable(
const struct nsm_fpga_authority_status *status)
{
if (status == NULL) {
return false;
}
const uint8_t need = NSM_FPGA_AUTH_QUARANTINED |
NSM_FPGA_AUTH_QUIESCENT | NSM_FPGA_AUTH_RELEASED;
return (status->flags & need) == need &&
status->owner == NSM_FPGA_OWNER_NSM;
}
bool nsm_fpga_authority_activatable(
const struct nsm_fpga_authority_status *status)
{
if (status == NULL) {
return false;
}
const uint8_t never = NSM_FPGA_AUTH_INITIALIZED |
NSM_FPGA_AUTH_QUARANTINED |
NSM_FPGA_AUTH_GRANT_VALID;
return (status->flags & never) == 0 &&
status->owner == NSM_FPGA_OWNER_NONE && status->epoch == 0;
}
enum nsm_fpga_session_state
nsm_fpga_session_classify(const struct nsm_fpga_authority_status *status)
{
if (status == NULL) {
return NSM_FPGA_SESSION_UNREACHABLE;
}
if (status->flags & NSM_FPGA_AUTH_QUARANTINED) {
return NSM_FPGA_SESSION_QUARANTINED;
}
if (status->epoch == 0 ||
!(status->flags & NSM_FPGA_AUTH_INITIALIZED)) {
return NSM_FPGA_SESSION_UNINITIALIZED;
}
if ((status->flags & NSM_FPGA_AUTH_GRANT_VALID) ||
status->owner == NSM_FPGA_OWNER_NSM) {
return NSM_FPGA_SESSION_OWNED_BY_BMC;
}
if (status->owner == NSM_FPGA_OWNER_NATIVE) {
return NSM_FPGA_SESSION_OWNED_BY_HOST;
}
if (status->flags & NSM_FPGA_AUTH_AVAILABLE) {
return NSM_FPGA_SESSION_READY;
}
return NSM_FPGA_SESSION_UNINITIALIZED;
}
int nsm_fpga_grant_from_authority(
const struct nsm_fpga_authority_status *status, uint32_t session,
uint64_t *epoch, uint32_t *generation)
{
if (status == NULL || epoch == NULL || generation == NULL) {
return NSM_SW_ERROR_NULL;
}
if (!nsm_fpga_authority_acquirable(status) || session == 0 ||
status->generation == UINT32_MAX) {
return NSM_SW_ERROR_DATA;
}
*epoch = status->epoch;
*generation = status->generation + 1;
return NSM_SW_SUCCESS;
}
const char *nsm_fpga_control_result_name(uint8_t result)
{
switch (result) {
case NSM_FPGA_CTRL_OK:
return "OK";
case NSM_FPGA_CTRL_INVALID:
return "INVALID";
case NSM_FPGA_CTRL_BUSY:
return "BUSY";
case NSM_FPGA_CTRL_STALE:
return "STALE";
case NSM_FPGA_CTRL_UNSAFE:
return "UNSAFE";
case NSM_FPGA_CTRL_DENIED:
return "DENIED";
case NSM_FPGA_CTRL_FENCED:
return "FENCED";
case NSM_FPGA_CTRL_EXHAUSTED:
return "EXHAUSTED";
default:
return "UNKNOWN";
}
}
struct nsm_fpga_identity
nsm_fpga_plan_identity(const struct nsm_fpga_grant *grant, uint32_t sequence)
{
struct nsm_fpga_identity identity = {0, 0, 0, 0};
if (grant != NULL) {
identity.epoch = grant->epoch;
identity.session = grant->session;
identity.generation = grant->generation;
identity.sequence = sequence;
}
return identity;
}
static void read_step(struct nsm_fpga_step *step, uint64_t address)
{
memset(step, 0, sizeof(*step));
step->kind = NSM_FPGA_STEP_READ;
step->address = address;
}
int nsm_fpga_plan_control(const struct nsm_fpga_window *window,
const struct nsm_fpga_grant *grant, uint8_t opcode,
uint32_t control_sequence, uint32_t argument,
struct nsm_fpga_step *steps)
{
if (window == NULL || grant == NULL || steps == NULL) {
return NSM_SW_ERROR_NULL;
}
const struct nsm_fpga_identity identity =
nsm_fpga_plan_identity(grant, control_sequence);
memset(steps, 0, NSM_FPGA_PLAN_CONTROL_STEPS * sizeof(*steps));
steps[0].kind = NSM_FPGA_STEP_WRITE;
steps[0].address = window->control_write;
int rc = nsm_fpga_encode_control(&identity, opcode, argument,
steps[0].record);
if (rc != NSM_SW_SUCCESS) {
return rc;
}
read_step(&steps[1], window->control_read);
return NSM_SW_SUCCESS;
}
int nsm_fpga_plan_authority(const struct nsm_fpga_window *window,
uint8_t opcode, uint64_t epoch,
struct nsm_fpga_step *steps)
{
if (window == NULL || steps == NULL) {
return NSM_SW_ERROR_NULL;
}
if (opcode != NSM_FPGA_CTRL_ACTIVATE &&
opcode != NSM_FPGA_CTRL_RECOVER) {
return NSM_SW_ERROR_DATA;
}
const struct nsm_fpga_identity identity = {epoch, 0, 0, 0};
memset(steps, 0, NSM_FPGA_PLAN_CONTROL_STEPS * sizeof(*steps));
steps[0].kind = NSM_FPGA_STEP_WRITE;
steps[0].address = window->control_write;
int rc = nsm_fpga_encode_control(&identity, opcode, 0, steps[0].record);
if (rc != NSM_SW_SUCCESS) {
return rc;
}
read_step(&steps[1], window->control_read);
return NSM_SW_SUCCESS;
}
int nsm_fpga_plan_recover(const struct nsm_fpga_window *window, uint64_t epoch,
struct nsm_fpga_step *steps)
{
return nsm_fpga_plan_authority(window, NSM_FPGA_CTRL_RECOVER, epoch,
steps);
}
int nsm_fpga_encode_word_compact(const struct nsm_fpga_identity *identity,
uint8_t station, uint8_t opcode, uint8_t mask,
uint64_t address, uint32_t data,
uint8_t *record)
{
if (record == NULL) {
return NSM_SW_ERROR_NULL;
}
if (!nsm_fpga_identity_valid(identity) || (address & 3) != 0 ||
address > UINT32_MAX) {
return NSM_SW_ERROR_DATA;
}
if (opcode == NSM_FPGA_OP_READ32) {
if (mask != 0x0f || data != 0) {
return NSM_SW_ERROR_DATA;
}
} else if (opcode == NSM_FPGA_OP_WRITE32) {
if (mask == 0 || mask > 0x0f) {
return NSM_SW_ERROR_DATA;
}
for (unsigned i = 0; i < 4; ++i) {
/* a byte the mask disables must be sent as zero */
if ((mask & (1u << i)) == 0 &&
((data >> (8 * i)) & 0xff) != 0) {
return NSM_SW_ERROR_DATA;
}
}
} else {
return NSM_SW_ERROR_DATA;
}
memset(record, 0, NSM_FPGA_RECORD_SIZE);
put_identity(record, COMPACT_IDENTITY, identity);
record[COMPACT_STATION] = station;
record[COMPACT_OPCODE] = opcode;
record[COMPACT_MASK] = mask;
record[COMPACT_VERSION] = NSM_FPGA_COMPACT_RECORD_VERSION;
put_le(record, COMPACT_ADDRESS, address, sizeof(uint32_t));
put_le(record, COMPACT_DATA, data, sizeof(data));
return NSM_SW_SUCCESS;
}
int nsm_fpga_plan_word_compact(const struct nsm_fpga_window *window,
const struct nsm_fpga_grant *grant,
uint32_t data_sequence, uint8_t station,
uint8_t opcode, uint64_t address, uint32_t value,
struct nsm_fpga_step *steps)
{
if (window == NULL || grant == NULL || steps == NULL) {
return NSM_SW_ERROR_NULL;
}
if (window->compact == 0) {
return NSM_SW_ERROR_DATA;
}
const struct nsm_fpga_identity identity =
nsm_fpga_plan_identity(grant, data_sequence);
memset(steps, 0, NSM_FPGA_PLAN_COMPACT_STEPS * sizeof(*steps));
steps[0].kind = NSM_FPGA_STEP_WRITE;
steps[0].address = window->compact;
int rc = nsm_fpga_encode_word_compact(
&identity, station, opcode, 0x0f, address,
opcode == NSM_FPGA_OP_WRITE32 ? value : 0, steps[0].record);
if (rc != NSM_SW_SUCCESS) {
return rc;
}
read_step(&steps[1], window->completion_read);
return NSM_SW_SUCCESS;
}
bool nsm_fpga_i2c_argument_valid(uint32_t argument)
{
const uint8_t address = (uint8_t)(argument & 0x7f);
const uint8_t data = (uint8_t)((argument >> 8) & 0xff);
const bool read = ((argument >> 16) & 1) != 0;
const bool stop = ((argument >> 18) & 1) != 0;
const bool read_nack = ((argument >> 19) & 1) != 0;
if ((argument & 0x80) != 0 || (argument >> 20) != 0) {
return false;
}
if (address < 0x08 || address > 0x77) {
return false;
}
if (read) {
if (data != 0 || (stop && !read_nack)) {
return false;
}
} else if (read_nack) {
return false;
}
return true;
}
int nsm_fpga_encode_byte_header(const struct nsm_fpga_identity *identity,
uint8_t station, uint32_t argument,
uint8_t *record)
{
if (record == NULL) {
return NSM_SW_ERROR_NULL;
}
if (!nsm_fpga_identity_valid(identity) ||
!nsm_fpga_i2c_argument_valid(argument)) {
return NSM_SW_ERROR_DATA;
}
memset(record, 0, NSM_FPGA_RECORD_SIZE);
put_le(record, REC_PROFILE, NSM_FPGA_PROFILE_IBC2, PROFILE_BYTES);
record[REC_OPCODE] = NSM_FPGA_OP_BYTE;
record[REC_STATION] = station;
record[REC_MASK] = 0;
put_identity(record, REC_IDENTITY, identity);
put_le(record, REC_ARGUMENT, argument, sizeof(argument));
return NSM_SW_SUCCESS;
}
int nsm_fpga_plan_i2c_byte(const struct nsm_fpga_window *window,
const struct nsm_fpga_grant *grant,
uint32_t data_sequence, uint8_t station,
uint32_t argument, struct nsm_fpga_step *steps)
{
if (window == NULL || grant == NULL || steps == NULL) {
return NSM_SW_ERROR_NULL;
}
const struct nsm_fpga_identity identity =
nsm_fpga_plan_identity(grant, data_sequence);
memset(steps, 0, NSM_FPGA_PLAN_BYTE_STEPS * sizeof(*steps));
steps[0].kind = NSM_FPGA_STEP_WRITE;
steps[0].address = window->data_write;
int rc = nsm_fpga_encode_byte_header(&identity, station, argument,
steps[0].record);
if (rc != NSM_SW_SUCCESS) {
return rc;
}
read_step(&steps[1], window->completion_read);
return NSM_SW_SUCCESS;
}
bool nsm_fpga_xfer_opcode_valid(uint8_t opcode)
{
const uint8_t writes = (opcode >> 3) & 0x3;
const uint8_t reads = opcode & 0x7;
return (opcode >> 5) == 2 && reads <= 4 && (opcode & 0x1f) != 0 &&
(writes + reads) > 0;
}
static bool xfer_argument_valid(uint8_t opcode, uint32_t argument)
{
const uint8_t address = (uint8_t)(argument & 0x7f);
const uint8_t writes = (opcode >> 3) & 0x3;
if ((argument & 0x80) != 0 || address < 0x08 || address > 0x77) {
return false;
}
/* no write bytes beyond the count the opcode announces */
return writes == 3 || (argument >> (8 + 8 * writes)) == 0;
}
int nsm_fpga_encode_xfer_header(const struct nsm_fpga_identity *identity,
uint8_t station, uint8_t opcode,
uint32_t argument, uint8_t *record)
{
if (record == NULL) {
return NSM_SW_ERROR_NULL;
}
if (!nsm_fpga_identity_valid(identity) ||
!nsm_fpga_xfer_opcode_valid(opcode) ||
!xfer_argument_valid(opcode, argument)) {
return NSM_SW_ERROR_DATA;
}
memset(record, 0, NSM_FPGA_RECORD_SIZE);
put_le(record, REC_PROFILE, NSM_FPGA_PROFILE_IBC2, PROFILE_BYTES);
record[REC_OPCODE] = opcode;
record[REC_STATION] = station;
record[REC_MASK] = 0;
put_identity(record, REC_IDENTITY, identity);
put_le(record, REC_ARGUMENT, argument, sizeof(argument));
return NSM_SW_SUCCESS;
}
int nsm_fpga_plan_i2c_xfer(const struct nsm_fpga_window *window,
const struct nsm_fpga_grant *grant,
uint32_t data_sequence, uint8_t station,
uint8_t opcode, uint32_t argument,
struct nsm_fpga_step *steps)
{
if (window == NULL || grant == NULL || steps == NULL) {
return NSM_SW_ERROR_NULL;
}
const struct nsm_fpga_identity identity =
nsm_fpga_plan_identity(grant, data_sequence);
memset(steps, 0, NSM_FPGA_PLAN_BYTE_STEPS * sizeof(*steps));
steps[0].kind = NSM_FPGA_STEP_WRITE;
steps[0].address = window->data_write;
int rc = nsm_fpga_encode_xfer_header(&identity, station, opcode,
argument, steps[0].record);
if (rc != NSM_SW_SUCCESS) {
return rc;
}
read_step(&steps[1], window->completion_read);
return NSM_SW_SUCCESS;
}
enum nsm_fpga_reply nsm_fpga_classify_i2c_xfer(
const uint8_t *record, const struct nsm_fpga_identity *identity,
uint8_t opcode, uint32_t *data, bool *nack, uint8_t *fault_reason)
{
struct nsm_fpga_word_completion c;
if (record == NULL || identity == NULL || nack == NULL ||
nsm_fpga_decode_word_completion(record, &c) != NSM_SW_SUCCESS) {
return NSM_FPGA_REPLY_MALFORMED;
}
*nack = false;
if (c.result == NSM_FPGA_RESULT_EMPTY) {
return NSM_FPGA_REPLY_PENDING;
}
if (c.owner != NSM_FPGA_OWNER_NSM || c.opcode != opcode ||
!same_identity(&c.identity, identity)) {
return NSM_FPGA_REPLY_FOREIGN;
}
const uint8_t reads = opcode & 0x7;
const uint8_t released = NSM_FPGA_FLAG_OCCUPIED |
NSM_FPGA_FLAG_QUIESCENT |
NSM_FPGA_FLAG_BUS_RELEASED;
switch (c.result) {
case NSM_FPGA_RESULT_PENDING:
if (c.flags ==
(NSM_FPGA_FLAG_OCCUPIED | NSM_FPGA_FLAG_PENDING) &&
c.data == 0) {
return NSM_FPGA_REPLY_PENDING;
}
return NSM_FPGA_REPLY_REJECTED;
case NSM_FPGA_RESULT_SUCCESS:
if (c.flags != (reads ? (released | NSM_FPGA_FLAG_READ_VALID)
: released) ||
(reads < 4 && (c.data >> (8 * reads)) != 0)) {
return NSM_FPGA_REPLY_REJECTED;
}
if (data != NULL) {
*data = c.data;
}
return NSM_FPGA_REPLY_DONE;
case NSM_FPGA_RESULT_TARGET_NACK:
/* the controller ended the transaction and released the bus */
if (c.flags != released || (c.data & 0xff) != 1) {
return NSM_FPGA_REPLY_REJECTED;
}
*nack = true;
return NSM_FPGA_REPLY_DONE;
case NSM_FPGA_RESULT_FAULT:
if ((c.flags & NSM_FPGA_FLAG_FAULTED) == 0 ||
((c.data & 0xff) != 2 && (c.data & 0xff) != 3)) {
return NSM_FPGA_REPLY_REJECTED;
}
if (fault_reason != NULL) {
*fault_reason = (uint8_t)(c.data & 0xff);
}
return NSM_FPGA_REPLY_FAULT;
default:
return NSM_FPGA_REPLY_REJECTED;
}
}
int nsm_fpga_plan_status_read(const struct nsm_fpga_window *window,
struct nsm_fpga_step *step)
{
if (window == NULL || step == NULL) {
return NSM_SW_ERROR_NULL;
}
read_step(step, window->status);
return NSM_SW_SUCCESS;
}
static enum nsm_fpga_reply reply_of(enum nsm_fpga_match match)
{
switch (match) {
case NSM_FPGA_MATCH_SUCCESS:
return NSM_FPGA_REPLY_DONE;
case NSM_FPGA_MATCH_PENDING:
case NSM_FPGA_MATCH_EMPTY:
return NSM_FPGA_REPLY_PENDING;
case NSM_FPGA_MATCH_REJECTED:
return NSM_FPGA_REPLY_REJECTED;
case NSM_FPGA_MATCH_FAULT:
return NSM_FPGA_REPLY_FAULT;
case NSM_FPGA_MATCH_FOREIGN:
default:
return NSM_FPGA_REPLY_FOREIGN;
}
}
enum nsm_fpga_reply
nsm_fpga_classify_control(const uint8_t *record,
const struct nsm_fpga_identity *identity,
uint8_t opcode, uint32_t argument, uint8_t *result)
{
struct nsm_fpga_control_completion completion;
if (record == NULL || identity == NULL ||
nsm_fpga_decode_control_completion(record, &completion) !=
NSM_SW_SUCCESS) {
return NSM_FPGA_REPLY_MALFORMED;
}
const enum nsm_fpga_reply reply = reply_of(
nsm_fpga_match_control(&completion, identity, opcode, argument));
if (result != NULL && reply == NSM_FPGA_REPLY_REJECTED) {
*result = completion.result;
}
return reply;
}
enum nsm_fpga_reply nsm_fpga_classify_authority(const uint8_t *record,
uint64_t epoch, uint8_t opcode,
uint8_t *result)
{
struct nsm_fpga_control_completion completion;
if (record == NULL || nsm_fpga_decode_control_completion(
record, &completion) != NSM_SW_SUCCESS) {
return NSM_FPGA_REPLY_MALFORMED;
}
if (!completion.occupied) {
return NSM_FPGA_REPLY_PENDING;
}
if (completion.opcode != opcode || completion.identity.epoch != epoch ||
completion.argument != 0) {
return NSM_FPGA_REPLY_FOREIGN;
}
if (completion.pending) {
return NSM_FPGA_REPLY_PENDING;
}
if (completion.result != NSM_FPGA_CTRL_OK) {
if (result != NULL) {
*result = completion.result;
}
return NSM_FPGA_REPLY_REJECTED;
}
return NSM_FPGA_REPLY_DONE;
}
enum nsm_fpga_reply
nsm_fpga_classify_word(const uint8_t *record,
const struct nsm_fpga_identity *identity, uint8_t opcode,
uint32_t *data, uint8_t *fault_reason)
{
struct nsm_fpga_word_completion completion;
if (record == NULL || identity == NULL ||
nsm_fpga_decode_word_completion(record, &completion) !=
NSM_SW_SUCCESS) {
return NSM_FPGA_REPLY_MALFORMED;
}
const enum nsm_fpga_reply reply =
reply_of(nsm_fpga_match_word(&completion, identity, opcode));
if (data != NULL && reply == NSM_FPGA_REPLY_DONE) {
*data = completion.data;
}
if (fault_reason != NULL && reply == NSM_FPGA_REPLY_FAULT) {
*fault_reason = completion.fault_reason;
}
return reply;
}
enum nsm_fpga_reply
nsm_fpga_classify_i2c_byte(const uint8_t *record,
const struct nsm_fpga_identity *identity,
uint32_t argument, uint8_t *data_byte, bool *nack)
{
struct nsm_fpga_word_completion c;
if (record == NULL || identity == NULL || nack == NULL ||
nsm_fpga_decode_word_completion(record, &c) != NSM_SW_SUCCESS) {
return NSM_FPGA_REPLY_MALFORMED;
}
*nack = false;
if (c.result == NSM_FPGA_RESULT_EMPTY) {
return NSM_FPGA_REPLY_PENDING;
}
if (c.owner != NSM_FPGA_OWNER_NSM || c.opcode != NSM_FPGA_OP_BYTE ||
!same_identity(&c.identity, identity)) {
return NSM_FPGA_REPLY_FOREIGN;
}
const uint8_t flags = c.flags;
const bool read = ((argument >> 16) & 1) != 0;
const bool quiescent = (flags & NSM_FPGA_FLAG_QUIESCENT) != 0;
const bool faulted = (flags & NSM_FPGA_FLAG_FAULTED) != 0;
const bool read_valid = (flags & NSM_FPGA_FLAG_READ_VALID) != 0;
const bool bus_held = (flags & NSM_FPGA_FLAG_BUS_HELD) != 0;
const bool bus_released = (flags & NSM_FPGA_FLAG_BUS_RELEASED) != 0;
switch (c.result) {
case NSM_FPGA_RESULT_PENDING:
return NSM_FPGA_REPLY_PENDING;
case NSM_FPGA_RESULT_TARGET_NACK:
if (!quiescent || faulted || (bus_released && bus_held)) {
return NSM_FPGA_REPLY_REJECTED;
}
*nack = true;
return NSM_FPGA_REPLY_DONE;
case NSM_FPGA_RESULT_SUCCESS:
if (!quiescent || faulted || read_valid != read ||
(bus_released && bus_held)) {
return NSM_FPGA_REPLY_REJECTED;
}
if (data_byte != NULL) {
*data_byte = read ? (uint8_t)(c.data & 0xff) : 0;
}
return NSM_FPGA_REPLY_DONE;
case NSM_FPGA_RESULT_FAULT:
return NSM_FPGA_REPLY_FAULT;
default:
return NSM_FPGA_REPLY_REJECTED;
}
}
const char *nsm_fpga_reply_name(enum nsm_fpga_reply reply)
{
switch (reply) {
case NSM_FPGA_REPLY_DONE:
return "done";
case NSM_FPGA_REPLY_PENDING:
return "pending";
case NSM_FPGA_REPLY_REJECTED:
return "rejected";
case NSM_FPGA_REPLY_FAULT:
return "fault";
case NSM_FPGA_REPLY_FOREIGN:
return "foreign";
case NSM_FPGA_REPLY_MALFORMED:
return "malformed";
}
return "unknown";
}