// SPDX-License-Identifier: GPL-2.0-only /* Copyright(c) 2024 Intel Corporation */ #include #include #include "adf_mstate_mgr.h" #define ADF_MSTATE_MAGIC 0xADF5CAEA #define ADF_MSTATE_VERSION 0x1 struct adf_mstate_sect_h { u8 id[ADF_MSTATE_ID_LEN]; u32 size; u32 sub_sects; u8 state[]; }; u32 adf_mstate_state_size(struct adf_mstate_mgr *mgr) { return mgr->state - mgr->buf; } static inline u32 adf_mstate_avail_room(struct adf_mstate_mgr *mgr) { return mgr->buf + mgr->size - mgr->state; } void adf_mstate_mgr_init(struct adf_mstate_mgr *mgr, u8 *buf, u32 size) { mgr->buf = buf; mgr->state = buf; mgr->size = size; mgr->n_sects = 0; }; struct adf_mstate_mgr *adf_mstate_mgr_new(u8 *buf, u32 size) { struct adf_mstate_mgr *mgr; mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); if (!mgr) return NULL; adf_mstate_mgr_init(mgr, buf, size); return mgr; } void adf_mstate_mgr_destroy(struct adf_mstate_mgr *mgr) { kfree(mgr); } void adf_mstate_mgr_init_from_parent(struct adf_mstate_mgr *mgr, struct adf_mstate_mgr *p_mgr) { adf_mstate_mgr_init(mgr, p_mgr->state, p_mgr->size - adf_mstate_state_size(p_mgr)); } void adf_mstate_mgr_init_from_psect(struct adf_mstate_mgr *mgr, struct adf_mstate_sect_h *p_sect) { adf_mstate_mgr_init(mgr, p_sect->state, p_sect->size); mgr->n_sects = p_sect->sub_sects; } static void adf_mstate_preamble_init(struct adf_mstate_preh *preamble) { preamble->magic = ADF_MSTATE_MAGIC; preamble->version = ADF_MSTATE_VERSION; preamble->preh_len = sizeof(*preamble); preamble->size = 0; preamble->n_sects = 0; } /* default preambles checker */ static int adf_mstate_preamble_def_checker(struct adf_mstate_preh *preamble, void *opaque) { struct adf_mstate_mgr *mgr = opaque; if (preamble->magic != ADF_MSTATE_MAGIC || preamble->version > ADF_MSTATE_VERSION || preamble->preh_len > mgr->size) { pr_debug("QAT: LM - Invalid state (magic=%#x, version=%#x, hlen=%u), state_size=%u\n", preamble->magic, preamble->version, preamble->preh_len, mgr->size); return -EINVAL; } return 0; } struct adf_mstate_preh *adf_mstate_preamble_add(struct adf_mstate_mgr *mgr) { struct adf_mstate_preh *pre = (struct adf_mstate_preh *)mgr->buf; if (adf_mstate_avail_room(mgr) < sizeof(*pre)) { pr_err("QAT: LM - Not enough space for preamble\n"); return NULL; } adf_mstate_preamble_init(pre); mgr->state += pre->preh_len; return pre; } int adf_mstate_preamble_update(struct adf_mstate_mgr *mgr) { struct adf_mstate_preh *preamble = (struct adf_mstate_preh *)mgr->buf; preamble->size = adf_mstate_state_size(mgr) - preamble->preh_len; preamble->n_sects = mgr->n_sects; return 0; } static void adf_mstate_dump_sect(struct adf_mstate_sect_h *sect, const char *prefix) { pr_debug("QAT: LM - %s QAT state section %s\n", prefix, sect->id); print_hex_dump_debug("h-", DUMP_PREFIX_OFFSET, 16, 2, sect, sizeof(*sect), true); print_hex_dump_debug("s-", DUMP_PREFIX_OFFSET, 16, 2, sect->state, sect->size, true); } static inline void __adf_mstate_sect_update(struct adf_mstate_mgr *mgr, struct adf_mstate_sect_h *sect, u32 size, u32 n_subsects) { sect->size += size; sect->sub_sects += n_subsects; mgr->n_sects++; mgr->state += sect->size; adf_mstate_dump_sect(sect, "Add"); } void adf_mstate_sect_update(struct adf_mstate_mgr *p_mgr, struct adf_mstate_mgr *curr_mgr, struct adf_mstate_sect_h *sect) { __adf_mstate_sect_update(p_mgr, sect, adf_mstate_state_size(curr_mgr), curr_mgr->n_sects); } static struct adf_mstate_sect_h *adf_mstate_sect_add_header(struct adf_mstate_mgr *mgr, const char *id) { struct adf_mstate_sect_h *sect = (struct adf_mstate_sect_h *)(mgr->state); if (adf_mstate_avail_room(mgr) < sizeof(*sect)) { pr_debug("QAT: LM - Not enough space for header of QAT state sect %s\n", id); return NULL; } strscpy(sect->id, id, sizeof(sect->id)); sect->size = 0; sect->sub_sects = 0; mgr->state += sizeof(*sect); return sect; } struct adf_mstate_sect_h *adf_mstate_sect_add_vreg(struct adf_mstate_mgr *mgr, const char *id, struct adf_mstate_vreginfo *info) { struct adf_mstate_sect_h *sect; sect = adf_mstate_sect_add_header(mgr, id); if (!sect) return NULL; if (adf_mstate_avail_room(mgr) < info->size) { pr_debug("QAT: LM - Not enough space for QAT state sect %s, requires %u\n", id, info->size); return NULL; } memcpy(sect->state, info->addr, info->size); __adf_mstate_sect_update(mgr, sect, info->size, 0); return sect; } struct adf_mstate_sect_h *adf_mstate_sect_add(struct adf_mstate_mgr *mgr, const char *id, adf_mstate_populate populate, void *opaque) { struct adf_mstate_mgr sub_sects_mgr; struct adf_mstate_sect_h *sect; int avail_room, size; sect = adf_mstate_sect_add_header(mgr, id); if (!sect) return NULL; if (!populate) return sect; avail_room = adf_mstate_avail_room(mgr); adf_mstate_mgr_init_from_parent(&sub_sects_mgr, mgr); size = (*populate)(&sub_sects_mgr, sect->state, avail_room, opaque); if (size < 0) return NULL; size += adf_mstate_state_size(&sub_sects_mgr); if (avail_room < size) { pr_debug("QAT: LM - Not enough space for QAT state sect %s, requires %u\n", id, size); return NULL; } __adf_mstate_sect_update(mgr, sect, size, sub_sects_mgr.n_sects); return sect; } static int adf_mstate_sect_validate(struct adf_mstate_mgr *mgr) { struct adf_mstate_sect_h *start = (struct adf_mstate_sect_h *)mgr->state; struct adf_mstate_sect_h *sect = start; u64 end; int i; end = (uintptr_t)mgr->buf + mgr->size; for (i = 0; i < mgr->n_sects; i++) { uintptr_t s_start = (uintptr_t)sect->state; uintptr_t s_end = s_start + sect->size; if (s_end < s_start || s_end > end) { pr_debug("QAT: LM - Corrupted state section (index=%u, size=%u) in state_mgr (size=%u, secs=%u)\n", i, sect->size, mgr->size, mgr->n_sects); return -EINVAL; } sect = (struct adf_mstate_sect_h *)s_end; } pr_debug("QAT: LM - Scanned section (last child=%s, size=%lu) in state_mgr (size=%u, secs=%u)\n", start->id, sizeof(struct adf_mstate_sect_h) * (ulong)(sect - start), mgr->size, mgr->n_sects); return 0; } u32 adf_mstate_state_size_from_remote(struct adf_mstate_mgr *mgr) { struct adf_mstate_preh *preh = (struct adf_mstate_preh *)mgr->buf; return preh->preh_len + preh->size; } int adf_mstate_mgr_init_from_remote(struct adf_mstate_mgr *mgr, u8 *buf, u32 size, adf_mstate_preamble_checker pre_checker, void *opaque) { struct adf_mstate_preh *pre; int ret; adf_mstate_mgr_init(mgr, buf, size); pre = (struct adf_mstate_preh *)(mgr->buf); pr_debug("QAT: LM - Dump state preambles\n"); print_hex_dump_debug("", DUMP_PREFIX_OFFSET, 16, 2, pre, pre->preh_len, 0); if (pre_checker) ret = (*pre_checker)(pre, opaque); else ret = adf_mstate_preamble_def_checker(pre, mgr); if (ret) return ret; mgr->state = mgr->buf + pre->preh_len; mgr->n_sects = pre->n_sects; return adf_mstate_sect_validate(mgr); } struct adf_mstate_sect_h *adf_mstate_sect_lookup(struct adf_mstate_mgr *mgr, const char *id, adf_mstate_action action, void *opaque) { struct adf_mstate_sect_h *sect = (struct adf_mstate_sect_h *)mgr->state; struct adf_mstate_mgr sub_sects_mgr; int i, ret; for (i = 0; i < mgr->n_sects; i++) { if (!strncmp(sect->id, id, sizeof(sect->id))) goto found; sect = (struct adf_mstate_sect_h *)(sect->state + sect->size); } return NULL; found: adf_mstate_dump_sect(sect, "Found"); adf_mstate_mgr_init_from_psect(&sub_sects_mgr, sect); if (sect->sub_sects && adf_mstate_sect_validate(&sub_sects_mgr)) return NULL; if (!action) return sect; ret = (*action)(&sub_sects_mgr, sect->state, sect->size, opaque); if (ret) return NULL; return sect; }