Commit c6203e67 authored by Alexander Duyck's avatar Alexander Duyck Committed by Jakub Kicinski

eth: fbnic: Add message parsing for FW messages

Add FW message formatting and parsing. The TLV format should
look very familiar to those familiar with netlink.
Since we don't have to deal with backward compatibility
we tweaked the format a little to make it easier to deal
with, and more appropriate for tightly coupled interfaces
like driver<>FW communication.
Signed-off-by: default avatarAlexander Duyck <alexanderduyck@fb.com>
Link: https://patch.msgid.link/172079936754.1778861.1029830244010564007.stgit@ahduyck-xeon-server.home.arpaSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 36461531
...@@ -10,4 +10,5 @@ obj-$(CONFIG_FBNIC) += fbnic.o ...@@ -10,4 +10,5 @@ obj-$(CONFIG_FBNIC) += fbnic.o
fbnic-y := fbnic_devlink.o \ fbnic-y := fbnic_devlink.o \
fbnic_irq.o \ fbnic_irq.o \
fbnic_mac.o \ fbnic_mac.o \
fbnic_pci.o fbnic_pci.o \
fbnic_tlv.o
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) Meta Platforms, Inc. and affiliates. */
#ifndef _FBNIC_TLV_H_
#define _FBNIC_TLV_H_
#include <asm/byteorder.h>
#include <linux/bits.h>
#include <linux/const.h>
#include <linux/types.h>
#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_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment