/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (c) Meta Platforms, Inc. and affiliates. */ #ifndef _FBNIC_TLV_H_ #define _FBNIC_TLV_H_ #include #include #include #include #define FBNIC_TLV_MSG_ALIGN(len) ALIGN(len, sizeof(u32)) #define FBNIC_TLV_MSG_SIZE(len) \ (FBNIC_TLV_MSG_ALIGN(len) / sizeof(u32)) /* TLV Header Format * 3 2 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Length |M|I|RSV| Type / ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * The TLV header format described above will be used for transferring * messages between the host and the firmware. To ensure byte ordering * we have defined all fields as being little endian. * Type/ID: Identifier for message and/or attribute * RSV: Reserved field for future use, likely as additional flags * I: cannot_ignore flag, identifies if unrecognized attribute can be ignored * M: is_msg, indicates that this is the start of a new message * Length: Total length of message in dwords including header * or * Total length of attribute in bytes including header */ struct fbnic_tlv_hdr { #if defined(__LITTLE_ENDIAN_BITFIELD) u16 type : 12; /* 0 .. 11 Type / ID */ u16 rsvd : 2; /* 12 .. 13 Reserved for future use */ u16 cannot_ignore : 1; /* 14 Attribute can be ignored */ u16 is_msg : 1; /* 15 Header belongs to message */ #elif defined(__BIG_ENDIAN_BITFIELD) u16 is_msg : 1; /* 15 Header belongs to message */ u16 cannot_ignore : 1; /* 14 Attribute can be ignored */ u16 rsvd : 2; /* 13 .. 12 Reserved for future use */ u16 type : 12; /* 11 .. 0 Type / ID */ #else #error "Missing defines from byteorder.h" #endif __le16 len; /* 16 .. 32 length including TLV header */ }; #define FBNIC_TLV_RESULTS_MAX 32 struct fbnic_tlv_msg { struct fbnic_tlv_hdr hdr; __le32 value[]; }; #define FBNIC_TLV_MSG_ID_UNKNOWN USHRT_MAX enum fbnic_tlv_type { FBNIC_TLV_STRING, FBNIC_TLV_FLAG, FBNIC_TLV_UNSIGNED, FBNIC_TLV_SIGNED, FBNIC_TLV_BINARY, FBNIC_TLV_NESTED, FBNIC_TLV_ARRAY, __FBNIC_TLV_MAX_TYPE }; /* TLV Index * Defines the relationship between the attribute IDs and their types. * For each entry in the index there will be a size and type associated * with it so that we can use this to parse the data and verify it matches * the expected layout. */ struct fbnic_tlv_index { u16 id; u16 len; enum fbnic_tlv_type type; }; #define TLV_MAX_DATA (PAGE_SIZE - 512) #define FBNIC_TLV_ATTR_ID_UNKNOWN USHRT_MAX #define FBNIC_TLV_ATTR_STRING(id, len) { id, len, FBNIC_TLV_STRING } #define FBNIC_TLV_ATTR_FLAG(id) { id, 0, FBNIC_TLV_FLAG } #define FBNIC_TLV_ATTR_U32(id) { id, sizeof(u32), FBNIC_TLV_UNSIGNED } #define FBNIC_TLV_ATTR_U64(id) { id, sizeof(u64), FBNIC_TLV_UNSIGNED } #define FBNIC_TLV_ATTR_S32(id) { id, sizeof(s32), FBNIC_TLV_SIGNED } #define FBNIC_TLV_ATTR_S64(id) { id, sizeof(s64), FBNIC_TLV_SIGNED } #define FBNIC_TLV_ATTR_MAC_ADDR(id) { id, ETH_ALEN, FBNIC_TLV_BINARY } #define FBNIC_TLV_ATTR_NESTED(id) { id, 0, FBNIC_TLV_NESTED } #define FBNIC_TLV_ATTR_ARRAY(id) { id, 0, FBNIC_TLV_ARRAY } #define FBNIC_TLV_ATTR_RAW_DATA(id) { id, TLV_MAX_DATA, FBNIC_TLV_BINARY } #define FBNIC_TLV_ATTR_LAST { FBNIC_TLV_ATTR_ID_UNKNOWN, 0, 0 } struct fbnic_tlv_parser { u16 id; const struct fbnic_tlv_index *attr; int (*func)(void *opaque, struct fbnic_tlv_msg **results); }; #define FBNIC_TLV_PARSER(id, attr, func) { FBNIC_TLV_MSG_ID_##id, attr, func } static inline void * fbnic_tlv_attr_get_value_ptr(struct fbnic_tlv_msg *attr) { return (void *)&attr->value[0]; } static inline bool fbnic_tlv_attr_get_bool(struct fbnic_tlv_msg *attr) { return !!attr; } u64 fbnic_tlv_attr_get_unsigned(struct fbnic_tlv_msg *attr); s64 fbnic_tlv_attr_get_signed(struct fbnic_tlv_msg *attr); size_t fbnic_tlv_attr_get_string(struct fbnic_tlv_msg *attr, char *str, size_t max_size); #define get_unsigned_result(id, location) \ do { \ struct fbnic_tlv_msg *result = results[id]; \ if (result) \ location = fbnic_tlv_attr_get_unsigned(result); \ } while (0) #define get_signed_result(id, location) \ do { \ struct fbnic_tlv_msg *result = results[id]; \ if (result) \ location = fbnic_tlv_attr_get_signed(result); \ } while (0) #define get_string_result(id, size, str, max_size) \ do { \ struct fbnic_tlv_msg *result = results[id]; \ if (result) \ size = fbnic_tlv_attr_get_string(result, str, max_size); \ } while (0) #define get_bool(id) (!!(results[id])) struct fbnic_tlv_msg *fbnic_tlv_msg_alloc(u16 msg_id); int fbnic_tlv_attr_put_flag(struct fbnic_tlv_msg *msg, const u16 attr_id); int fbnic_tlv_attr_put_value(struct fbnic_tlv_msg *msg, const u16 attr_id, const void *value, const int len); int __fbnic_tlv_attr_put_int(struct fbnic_tlv_msg *msg, const u16 attr_id, s64 value, const int len); #define fbnic_tlv_attr_put_int(msg, attr_id, value) \ __fbnic_tlv_attr_put_int(msg, attr_id, value, \ FBNIC_TLV_MSG_ALIGN(sizeof(value))) int fbnic_tlv_attr_put_mac_addr(struct fbnic_tlv_msg *msg, const u16 attr_id, const u8 *mac_addr); int fbnic_tlv_attr_put_string(struct fbnic_tlv_msg *msg, u16 attr_id, const char *string); struct fbnic_tlv_msg *fbnic_tlv_attr_nest_start(struct fbnic_tlv_msg *msg, u16 attr_id); void fbnic_tlv_attr_nest_stop(struct fbnic_tlv_msg *msg); void fbnic_tlv_attr_addr_copy(u8 *dest, struct fbnic_tlv_msg *src); int fbnic_tlv_attr_parse_array(struct fbnic_tlv_msg *attr, int len, struct fbnic_tlv_msg **results, const struct fbnic_tlv_index *tlv_index, u16 tlv_attr_id, size_t array_len); int fbnic_tlv_attr_parse(struct fbnic_tlv_msg *attr, int len, struct fbnic_tlv_msg **results, const struct fbnic_tlv_index *tlv_index); int fbnic_tlv_msg_parse(void *opaque, struct fbnic_tlv_msg *msg, const struct fbnic_tlv_parser *parser); int fbnic_tlv_parser_error(void *opaque, struct fbnic_tlv_msg **results); #define FBNIC_TLV_MSG_ERROR \ FBNIC_TLV_PARSER(UNKNOWN, NULL, fbnic_tlv_parser_error) #endif /* _FBNIC_TLV_H_ */