162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Thunderbolt XDomain property support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2017, Intel Corporation 662306a36Sopenharmony_ci * Authors: Michael Jamet <michael.jamet@intel.com> 762306a36Sopenharmony_ci * Mika Westerberg <mika.westerberg@linux.intel.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/string.h> 1362306a36Sopenharmony_ci#include <linux/uuid.h> 1462306a36Sopenharmony_ci#include <linux/thunderbolt.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistruct tb_property_entry { 1762306a36Sopenharmony_ci u32 key_hi; 1862306a36Sopenharmony_ci u32 key_lo; 1962306a36Sopenharmony_ci u16 length; 2062306a36Sopenharmony_ci u8 reserved; 2162306a36Sopenharmony_ci u8 type; 2262306a36Sopenharmony_ci u32 value; 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct tb_property_rootdir_entry { 2662306a36Sopenharmony_ci u32 magic; 2762306a36Sopenharmony_ci u32 length; 2862306a36Sopenharmony_ci struct tb_property_entry entries[]; 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct tb_property_dir_entry { 3262306a36Sopenharmony_ci u32 uuid[4]; 3362306a36Sopenharmony_ci struct tb_property_entry entries[]; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define TB_PROPERTY_ROOTDIR_MAGIC 0x55584401 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic struct tb_property_dir *__tb_property_parse_dir(const u32 *block, 3962306a36Sopenharmony_ci size_t block_len, unsigned int dir_offset, size_t dir_len, 4062306a36Sopenharmony_ci bool is_root); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic inline void parse_dwdata(void *dst, const void *src, size_t dwords) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci be32_to_cpu_array(dst, src, dwords); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic inline void format_dwdata(void *dst, const void *src, size_t dwords) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci cpu_to_be32_array(dst, src, dwords); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic bool tb_property_entry_valid(const struct tb_property_entry *entry, 5362306a36Sopenharmony_ci size_t block_len) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci switch (entry->type) { 5662306a36Sopenharmony_ci case TB_PROPERTY_TYPE_DIRECTORY: 5762306a36Sopenharmony_ci case TB_PROPERTY_TYPE_DATA: 5862306a36Sopenharmony_ci case TB_PROPERTY_TYPE_TEXT: 5962306a36Sopenharmony_ci if (entry->length > block_len) 6062306a36Sopenharmony_ci return false; 6162306a36Sopenharmony_ci if (entry->value + entry->length > block_len) 6262306a36Sopenharmony_ci return false; 6362306a36Sopenharmony_ci break; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci case TB_PROPERTY_TYPE_VALUE: 6662306a36Sopenharmony_ci if (entry->length != 1) 6762306a36Sopenharmony_ci return false; 6862306a36Sopenharmony_ci break; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return true; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic bool tb_property_key_valid(const char *key) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci return key && strlen(key) <= TB_PROPERTY_KEY_SIZE; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic struct tb_property * 8062306a36Sopenharmony_citb_property_alloc(const char *key, enum tb_property_type type) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct tb_property *property; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci property = kzalloc(sizeof(*property), GFP_KERNEL); 8562306a36Sopenharmony_ci if (!property) 8662306a36Sopenharmony_ci return NULL; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci strcpy(property->key, key); 8962306a36Sopenharmony_ci property->type = type; 9062306a36Sopenharmony_ci INIT_LIST_HEAD(&property->list); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return property; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic struct tb_property *tb_property_parse(const u32 *block, size_t block_len, 9662306a36Sopenharmony_ci const struct tb_property_entry *entry) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci char key[TB_PROPERTY_KEY_SIZE + 1]; 9962306a36Sopenharmony_ci struct tb_property *property; 10062306a36Sopenharmony_ci struct tb_property_dir *dir; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (!tb_property_entry_valid(entry, block_len)) 10362306a36Sopenharmony_ci return NULL; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci parse_dwdata(key, entry, 2); 10662306a36Sopenharmony_ci key[TB_PROPERTY_KEY_SIZE] = '\0'; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci property = tb_property_alloc(key, entry->type); 10962306a36Sopenharmony_ci if (!property) 11062306a36Sopenharmony_ci return NULL; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci property->length = entry->length; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci switch (property->type) { 11562306a36Sopenharmony_ci case TB_PROPERTY_TYPE_DIRECTORY: 11662306a36Sopenharmony_ci dir = __tb_property_parse_dir(block, block_len, entry->value, 11762306a36Sopenharmony_ci entry->length, false); 11862306a36Sopenharmony_ci if (!dir) { 11962306a36Sopenharmony_ci kfree(property); 12062306a36Sopenharmony_ci return NULL; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci property->value.dir = dir; 12362306a36Sopenharmony_ci break; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci case TB_PROPERTY_TYPE_DATA: 12662306a36Sopenharmony_ci property->value.data = kcalloc(property->length, sizeof(u32), 12762306a36Sopenharmony_ci GFP_KERNEL); 12862306a36Sopenharmony_ci if (!property->value.data) { 12962306a36Sopenharmony_ci kfree(property); 13062306a36Sopenharmony_ci return NULL; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci parse_dwdata(property->value.data, block + entry->value, 13362306a36Sopenharmony_ci entry->length); 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci case TB_PROPERTY_TYPE_TEXT: 13762306a36Sopenharmony_ci property->value.text = kcalloc(property->length, sizeof(u32), 13862306a36Sopenharmony_ci GFP_KERNEL); 13962306a36Sopenharmony_ci if (!property->value.text) { 14062306a36Sopenharmony_ci kfree(property); 14162306a36Sopenharmony_ci return NULL; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci parse_dwdata(property->value.text, block + entry->value, 14462306a36Sopenharmony_ci entry->length); 14562306a36Sopenharmony_ci /* Force null termination */ 14662306a36Sopenharmony_ci property->value.text[property->length * 4 - 1] = '\0'; 14762306a36Sopenharmony_ci break; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci case TB_PROPERTY_TYPE_VALUE: 15062306a36Sopenharmony_ci property->value.immediate = entry->value; 15162306a36Sopenharmony_ci break; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci default: 15462306a36Sopenharmony_ci property->type = TB_PROPERTY_TYPE_UNKNOWN; 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return property; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic struct tb_property_dir *__tb_property_parse_dir(const u32 *block, 16262306a36Sopenharmony_ci size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci const struct tb_property_entry *entries; 16562306a36Sopenharmony_ci size_t i, content_len, nentries; 16662306a36Sopenharmony_ci unsigned int content_offset; 16762306a36Sopenharmony_ci struct tb_property_dir *dir; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci dir = kzalloc(sizeof(*dir), GFP_KERNEL); 17062306a36Sopenharmony_ci if (!dir) 17162306a36Sopenharmony_ci return NULL; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (is_root) { 17462306a36Sopenharmony_ci content_offset = dir_offset + 2; 17562306a36Sopenharmony_ci content_len = dir_len; 17662306a36Sopenharmony_ci } else { 17762306a36Sopenharmony_ci dir->uuid = kmemdup(&block[dir_offset], sizeof(*dir->uuid), 17862306a36Sopenharmony_ci GFP_KERNEL); 17962306a36Sopenharmony_ci if (!dir->uuid) { 18062306a36Sopenharmony_ci tb_property_free_dir(dir); 18162306a36Sopenharmony_ci return NULL; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci content_offset = dir_offset + 4; 18462306a36Sopenharmony_ci content_len = dir_len - 4; /* Length includes UUID */ 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci entries = (const struct tb_property_entry *)&block[content_offset]; 18862306a36Sopenharmony_ci nentries = content_len / (sizeof(*entries) / 4); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci INIT_LIST_HEAD(&dir->properties); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci for (i = 0; i < nentries; i++) { 19362306a36Sopenharmony_ci struct tb_property *property; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci property = tb_property_parse(block, block_len, &entries[i]); 19662306a36Sopenharmony_ci if (!property) { 19762306a36Sopenharmony_ci tb_property_free_dir(dir); 19862306a36Sopenharmony_ci return NULL; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci list_add_tail(&property->list, &dir->properties); 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return dir; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/** 20862306a36Sopenharmony_ci * tb_property_parse_dir() - Parses properties from given property block 20962306a36Sopenharmony_ci * @block: Property block to parse 21062306a36Sopenharmony_ci * @block_len: Number of dword elements in the property block 21162306a36Sopenharmony_ci * 21262306a36Sopenharmony_ci * This function parses the XDomain properties data block into format that 21362306a36Sopenharmony_ci * can be traversed using the helper functions provided by this module. 21462306a36Sopenharmony_ci * Upon success returns the parsed directory. In case of error returns 21562306a36Sopenharmony_ci * %NULL. The resulting &struct tb_property_dir needs to be released by 21662306a36Sopenharmony_ci * calling tb_property_free_dir() when not needed anymore. 21762306a36Sopenharmony_ci * 21862306a36Sopenharmony_ci * The @block is expected to be root directory. 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_cistruct tb_property_dir *tb_property_parse_dir(const u32 *block, 22162306a36Sopenharmony_ci size_t block_len) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci const struct tb_property_rootdir_entry *rootdir = 22462306a36Sopenharmony_ci (const struct tb_property_rootdir_entry *)block; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (rootdir->magic != TB_PROPERTY_ROOTDIR_MAGIC) 22762306a36Sopenharmony_ci return NULL; 22862306a36Sopenharmony_ci if (rootdir->length > block_len) 22962306a36Sopenharmony_ci return NULL; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return __tb_property_parse_dir(block, block_len, 0, rootdir->length, 23262306a36Sopenharmony_ci true); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/** 23662306a36Sopenharmony_ci * tb_property_create_dir() - Creates new property directory 23762306a36Sopenharmony_ci * @uuid: UUID used to identify the particular directory 23862306a36Sopenharmony_ci * 23962306a36Sopenharmony_ci * Creates new, empty property directory. If @uuid is %NULL then the 24062306a36Sopenharmony_ci * directory is assumed to be root directory. 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_cistruct tb_property_dir *tb_property_create_dir(const uuid_t *uuid) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct tb_property_dir *dir; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci dir = kzalloc(sizeof(*dir), GFP_KERNEL); 24762306a36Sopenharmony_ci if (!dir) 24862306a36Sopenharmony_ci return NULL; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci INIT_LIST_HEAD(&dir->properties); 25162306a36Sopenharmony_ci if (uuid) { 25262306a36Sopenharmony_ci dir->uuid = kmemdup(uuid, sizeof(*dir->uuid), GFP_KERNEL); 25362306a36Sopenharmony_ci if (!dir->uuid) { 25462306a36Sopenharmony_ci kfree(dir); 25562306a36Sopenharmony_ci return NULL; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci return dir; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_create_dir); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic void tb_property_free(struct tb_property *property) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci switch (property->type) { 26662306a36Sopenharmony_ci case TB_PROPERTY_TYPE_DIRECTORY: 26762306a36Sopenharmony_ci tb_property_free_dir(property->value.dir); 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci case TB_PROPERTY_TYPE_DATA: 27162306a36Sopenharmony_ci kfree(property->value.data); 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci case TB_PROPERTY_TYPE_TEXT: 27562306a36Sopenharmony_ci kfree(property->value.text); 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci default: 27962306a36Sopenharmony_ci break; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci kfree(property); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/** 28662306a36Sopenharmony_ci * tb_property_free_dir() - Release memory allocated for property directory 28762306a36Sopenharmony_ci * @dir: Directory to release 28862306a36Sopenharmony_ci * 28962306a36Sopenharmony_ci * This will release all the memory the directory occupies including all 29062306a36Sopenharmony_ci * descendants. It is OK to pass %NULL @dir, then the function does 29162306a36Sopenharmony_ci * nothing. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_civoid tb_property_free_dir(struct tb_property_dir *dir) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct tb_property *property, *tmp; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (!dir) 29862306a36Sopenharmony_ci return; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci list_for_each_entry_safe(property, tmp, &dir->properties, list) { 30162306a36Sopenharmony_ci list_del(&property->list); 30262306a36Sopenharmony_ci tb_property_free(property); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci kfree(dir->uuid); 30562306a36Sopenharmony_ci kfree(dir); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_free_dir); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic size_t tb_property_dir_length(const struct tb_property_dir *dir, 31062306a36Sopenharmony_ci bool recurse, size_t *data_len) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci const struct tb_property *property; 31362306a36Sopenharmony_ci size_t len = 0; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (dir->uuid) 31662306a36Sopenharmony_ci len += sizeof(*dir->uuid) / 4; 31762306a36Sopenharmony_ci else 31862306a36Sopenharmony_ci len += sizeof(struct tb_property_rootdir_entry) / 4; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci list_for_each_entry(property, &dir->properties, list) { 32162306a36Sopenharmony_ci len += sizeof(struct tb_property_entry) / 4; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci switch (property->type) { 32462306a36Sopenharmony_ci case TB_PROPERTY_TYPE_DIRECTORY: 32562306a36Sopenharmony_ci if (recurse) { 32662306a36Sopenharmony_ci len += tb_property_dir_length( 32762306a36Sopenharmony_ci property->value.dir, recurse, data_len); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci /* Reserve dword padding after each directory */ 33062306a36Sopenharmony_ci if (data_len) 33162306a36Sopenharmony_ci *data_len += 1; 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci case TB_PROPERTY_TYPE_DATA: 33562306a36Sopenharmony_ci case TB_PROPERTY_TYPE_TEXT: 33662306a36Sopenharmony_ci if (data_len) 33762306a36Sopenharmony_ci *data_len += property->length; 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci default: 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return len; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic ssize_t __tb_property_format_dir(const struct tb_property_dir *dir, 34962306a36Sopenharmony_ci u32 *block, unsigned int start_offset, size_t block_len) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci unsigned int data_offset, dir_end; 35262306a36Sopenharmony_ci const struct tb_property *property; 35362306a36Sopenharmony_ci struct tb_property_entry *entry; 35462306a36Sopenharmony_ci size_t dir_len, data_len = 0; 35562306a36Sopenharmony_ci int ret; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* 35862306a36Sopenharmony_ci * The structure of property block looks like following. Leaf 35962306a36Sopenharmony_ci * data/text is included right after the directory and each 36062306a36Sopenharmony_ci * directory follows each other (even nested ones). 36162306a36Sopenharmony_ci * 36262306a36Sopenharmony_ci * +----------+ <-- start_offset 36362306a36Sopenharmony_ci * | header | <-- root directory header 36462306a36Sopenharmony_ci * +----------+ --- 36562306a36Sopenharmony_ci * | entry 0 | -^--------------------. 36662306a36Sopenharmony_ci * +----------+ | | 36762306a36Sopenharmony_ci * | entry 1 | -|--------------------|--. 36862306a36Sopenharmony_ci * +----------+ | | | 36962306a36Sopenharmony_ci * | entry 2 | -|-----------------. | | 37062306a36Sopenharmony_ci * +----------+ | | | | 37162306a36Sopenharmony_ci * : : | dir_len | | | 37262306a36Sopenharmony_ci * . . | | | | 37362306a36Sopenharmony_ci * : : | | | | 37462306a36Sopenharmony_ci * +----------+ | | | | 37562306a36Sopenharmony_ci * | entry n | v | | | 37662306a36Sopenharmony_ci * +----------+ <-- data_offset | | | 37762306a36Sopenharmony_ci * | data 0 | <------------------|--' | 37862306a36Sopenharmony_ci * +----------+ | | 37962306a36Sopenharmony_ci * | data 1 | <------------------|-----' 38062306a36Sopenharmony_ci * +----------+ | 38162306a36Sopenharmony_ci * | 00000000 | padding | 38262306a36Sopenharmony_ci * +----------+ <-- dir_end <------' 38362306a36Sopenharmony_ci * | UUID | <-- directory UUID (child directory) 38462306a36Sopenharmony_ci * +----------+ 38562306a36Sopenharmony_ci * | entry 0 | 38662306a36Sopenharmony_ci * +----------+ 38762306a36Sopenharmony_ci * | entry 1 | 38862306a36Sopenharmony_ci * +----------+ 38962306a36Sopenharmony_ci * : : 39062306a36Sopenharmony_ci * . . 39162306a36Sopenharmony_ci * : : 39262306a36Sopenharmony_ci * +----------+ 39362306a36Sopenharmony_ci * | entry n | 39462306a36Sopenharmony_ci * +----------+ 39562306a36Sopenharmony_ci * | data 0 | 39662306a36Sopenharmony_ci * +----------+ 39762306a36Sopenharmony_ci * 39862306a36Sopenharmony_ci * We use dir_end to hold pointer to the end of the directory. It 39962306a36Sopenharmony_ci * will increase as we add directories and each directory should be 40062306a36Sopenharmony_ci * added starting from previous dir_end. 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ci dir_len = tb_property_dir_length(dir, false, &data_len); 40362306a36Sopenharmony_ci data_offset = start_offset + dir_len; 40462306a36Sopenharmony_ci dir_end = start_offset + data_len + dir_len; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (data_offset > dir_end) 40762306a36Sopenharmony_ci return -EINVAL; 40862306a36Sopenharmony_ci if (dir_end > block_len) 40962306a36Sopenharmony_ci return -EINVAL; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* Write headers first */ 41262306a36Sopenharmony_ci if (dir->uuid) { 41362306a36Sopenharmony_ci struct tb_property_dir_entry *pe; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci pe = (struct tb_property_dir_entry *)&block[start_offset]; 41662306a36Sopenharmony_ci memcpy(pe->uuid, dir->uuid, sizeof(pe->uuid)); 41762306a36Sopenharmony_ci entry = pe->entries; 41862306a36Sopenharmony_ci } else { 41962306a36Sopenharmony_ci struct tb_property_rootdir_entry *re; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci re = (struct tb_property_rootdir_entry *)&block[start_offset]; 42262306a36Sopenharmony_ci re->magic = TB_PROPERTY_ROOTDIR_MAGIC; 42362306a36Sopenharmony_ci re->length = dir_len - sizeof(*re) / 4; 42462306a36Sopenharmony_ci entry = re->entries; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci list_for_each_entry(property, &dir->properties, list) { 42862306a36Sopenharmony_ci const struct tb_property_dir *child; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci format_dwdata(entry, property->key, 2); 43162306a36Sopenharmony_ci entry->type = property->type; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci switch (property->type) { 43462306a36Sopenharmony_ci case TB_PROPERTY_TYPE_DIRECTORY: 43562306a36Sopenharmony_ci child = property->value.dir; 43662306a36Sopenharmony_ci ret = __tb_property_format_dir(child, block, dir_end, 43762306a36Sopenharmony_ci block_len); 43862306a36Sopenharmony_ci if (ret < 0) 43962306a36Sopenharmony_ci return ret; 44062306a36Sopenharmony_ci entry->length = tb_property_dir_length(child, false, 44162306a36Sopenharmony_ci NULL); 44262306a36Sopenharmony_ci entry->value = dir_end; 44362306a36Sopenharmony_ci dir_end = ret; 44462306a36Sopenharmony_ci break; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci case TB_PROPERTY_TYPE_DATA: 44762306a36Sopenharmony_ci format_dwdata(&block[data_offset], property->value.data, 44862306a36Sopenharmony_ci property->length); 44962306a36Sopenharmony_ci entry->length = property->length; 45062306a36Sopenharmony_ci entry->value = data_offset; 45162306a36Sopenharmony_ci data_offset += entry->length; 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci case TB_PROPERTY_TYPE_TEXT: 45562306a36Sopenharmony_ci format_dwdata(&block[data_offset], property->value.text, 45662306a36Sopenharmony_ci property->length); 45762306a36Sopenharmony_ci entry->length = property->length; 45862306a36Sopenharmony_ci entry->value = data_offset; 45962306a36Sopenharmony_ci data_offset += entry->length; 46062306a36Sopenharmony_ci break; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci case TB_PROPERTY_TYPE_VALUE: 46362306a36Sopenharmony_ci entry->length = property->length; 46462306a36Sopenharmony_ci entry->value = property->value.immediate; 46562306a36Sopenharmony_ci break; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci default: 46862306a36Sopenharmony_ci break; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci entry++; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci return dir_end; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci/** 47862306a36Sopenharmony_ci * tb_property_format_dir() - Formats directory to the packed XDomain format 47962306a36Sopenharmony_ci * @dir: Directory to format 48062306a36Sopenharmony_ci * @block: Property block where the packed data is placed 48162306a36Sopenharmony_ci * @block_len: Length of the property block 48262306a36Sopenharmony_ci * 48362306a36Sopenharmony_ci * This function formats the directory to the packed format that can be 48462306a36Sopenharmony_ci * then send over the thunderbolt fabric to receiving host. Returns %0 in 48562306a36Sopenharmony_ci * case of success and negative errno on faulure. Passing %NULL in @block 48662306a36Sopenharmony_ci * returns number of entries the block takes. 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_cissize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block, 48962306a36Sopenharmony_ci size_t block_len) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci ssize_t ret; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (!block) { 49462306a36Sopenharmony_ci size_t dir_len, data_len = 0; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci dir_len = tb_property_dir_length(dir, true, &data_len); 49762306a36Sopenharmony_ci return dir_len + data_len; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci ret = __tb_property_format_dir(dir, block, 0, block_len); 50162306a36Sopenharmony_ci return ret < 0 ? ret : 0; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci/** 50562306a36Sopenharmony_ci * tb_property_copy_dir() - Take a deep copy of directory 50662306a36Sopenharmony_ci * @dir: Directory to copy 50762306a36Sopenharmony_ci * 50862306a36Sopenharmony_ci * This function takes a deep copy of @dir and returns back the copy. In 50962306a36Sopenharmony_ci * case of error returns %NULL. The resulting directory needs to be 51062306a36Sopenharmony_ci * released by calling tb_property_free_dir(). 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_cistruct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct tb_property *property, *p = NULL; 51562306a36Sopenharmony_ci struct tb_property_dir *d; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (!dir) 51862306a36Sopenharmony_ci return NULL; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci d = tb_property_create_dir(dir->uuid); 52162306a36Sopenharmony_ci if (!d) 52262306a36Sopenharmony_ci return NULL; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci list_for_each_entry(property, &dir->properties, list) { 52562306a36Sopenharmony_ci struct tb_property *p; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci p = tb_property_alloc(property->key, property->type); 52862306a36Sopenharmony_ci if (!p) 52962306a36Sopenharmony_ci goto err_free; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci p->length = property->length; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci switch (property->type) { 53462306a36Sopenharmony_ci case TB_PROPERTY_TYPE_DIRECTORY: 53562306a36Sopenharmony_ci p->value.dir = tb_property_copy_dir(property->value.dir); 53662306a36Sopenharmony_ci if (!p->value.dir) 53762306a36Sopenharmony_ci goto err_free; 53862306a36Sopenharmony_ci break; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci case TB_PROPERTY_TYPE_DATA: 54162306a36Sopenharmony_ci p->value.data = kmemdup(property->value.data, 54262306a36Sopenharmony_ci property->length * 4, 54362306a36Sopenharmony_ci GFP_KERNEL); 54462306a36Sopenharmony_ci if (!p->value.data) 54562306a36Sopenharmony_ci goto err_free; 54662306a36Sopenharmony_ci break; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci case TB_PROPERTY_TYPE_TEXT: 54962306a36Sopenharmony_ci p->value.text = kzalloc(p->length * 4, GFP_KERNEL); 55062306a36Sopenharmony_ci if (!p->value.text) 55162306a36Sopenharmony_ci goto err_free; 55262306a36Sopenharmony_ci strcpy(p->value.text, property->value.text); 55362306a36Sopenharmony_ci break; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci case TB_PROPERTY_TYPE_VALUE: 55662306a36Sopenharmony_ci p->value.immediate = property->value.immediate; 55762306a36Sopenharmony_ci break; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci default: 56062306a36Sopenharmony_ci break; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci list_add_tail(&p->list, &d->properties); 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci return d; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cierr_free: 56962306a36Sopenharmony_ci kfree(p); 57062306a36Sopenharmony_ci tb_property_free_dir(d); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci return NULL; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci/** 57662306a36Sopenharmony_ci * tb_property_add_immediate() - Add immediate property to directory 57762306a36Sopenharmony_ci * @parent: Directory to add the property 57862306a36Sopenharmony_ci * @key: Key for the property 57962306a36Sopenharmony_ci * @value: Immediate value to store with the property 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_ciint tb_property_add_immediate(struct tb_property_dir *parent, const char *key, 58262306a36Sopenharmony_ci u32 value) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci struct tb_property *property; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (!tb_property_key_valid(key)) 58762306a36Sopenharmony_ci return -EINVAL; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci property = tb_property_alloc(key, TB_PROPERTY_TYPE_VALUE); 59062306a36Sopenharmony_ci if (!property) 59162306a36Sopenharmony_ci return -ENOMEM; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci property->length = 1; 59462306a36Sopenharmony_ci property->value.immediate = value; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci list_add_tail(&property->list, &parent->properties); 59762306a36Sopenharmony_ci return 0; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_add_immediate); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci/** 60262306a36Sopenharmony_ci * tb_property_add_data() - Adds arbitrary data property to directory 60362306a36Sopenharmony_ci * @parent: Directory to add the property 60462306a36Sopenharmony_ci * @key: Key for the property 60562306a36Sopenharmony_ci * @buf: Data buffer to add 60662306a36Sopenharmony_ci * @buflen: Number of bytes in the data buffer 60762306a36Sopenharmony_ci * 60862306a36Sopenharmony_ci * Function takes a copy of @buf and adds it to the directory. 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_ciint tb_property_add_data(struct tb_property_dir *parent, const char *key, 61162306a36Sopenharmony_ci const void *buf, size_t buflen) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci /* Need to pad to dword boundary */ 61462306a36Sopenharmony_ci size_t size = round_up(buflen, 4); 61562306a36Sopenharmony_ci struct tb_property *property; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (!tb_property_key_valid(key)) 61862306a36Sopenharmony_ci return -EINVAL; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci property = tb_property_alloc(key, TB_PROPERTY_TYPE_DATA); 62162306a36Sopenharmony_ci if (!property) 62262306a36Sopenharmony_ci return -ENOMEM; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci property->length = size / 4; 62562306a36Sopenharmony_ci property->value.data = kzalloc(size, GFP_KERNEL); 62662306a36Sopenharmony_ci if (!property->value.data) { 62762306a36Sopenharmony_ci kfree(property); 62862306a36Sopenharmony_ci return -ENOMEM; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci memcpy(property->value.data, buf, buflen); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci list_add_tail(&property->list, &parent->properties); 63462306a36Sopenharmony_ci return 0; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_add_data); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci/** 63962306a36Sopenharmony_ci * tb_property_add_text() - Adds string property to directory 64062306a36Sopenharmony_ci * @parent: Directory to add the property 64162306a36Sopenharmony_ci * @key: Key for the property 64262306a36Sopenharmony_ci * @text: String to add 64362306a36Sopenharmony_ci * 64462306a36Sopenharmony_ci * Function takes a copy of @text and adds it to the directory. 64562306a36Sopenharmony_ci */ 64662306a36Sopenharmony_ciint tb_property_add_text(struct tb_property_dir *parent, const char *key, 64762306a36Sopenharmony_ci const char *text) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci /* Need to pad to dword boundary */ 65062306a36Sopenharmony_ci size_t size = round_up(strlen(text) + 1, 4); 65162306a36Sopenharmony_ci struct tb_property *property; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (!tb_property_key_valid(key)) 65462306a36Sopenharmony_ci return -EINVAL; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci property = tb_property_alloc(key, TB_PROPERTY_TYPE_TEXT); 65762306a36Sopenharmony_ci if (!property) 65862306a36Sopenharmony_ci return -ENOMEM; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci property->length = size / 4; 66162306a36Sopenharmony_ci property->value.text = kzalloc(size, GFP_KERNEL); 66262306a36Sopenharmony_ci if (!property->value.text) { 66362306a36Sopenharmony_ci kfree(property); 66462306a36Sopenharmony_ci return -ENOMEM; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci strcpy(property->value.text, text); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci list_add_tail(&property->list, &parent->properties); 67062306a36Sopenharmony_ci return 0; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_add_text); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci/** 67562306a36Sopenharmony_ci * tb_property_add_dir() - Adds a directory to the parent directory 67662306a36Sopenharmony_ci * @parent: Directory to add the property 67762306a36Sopenharmony_ci * @key: Key for the property 67862306a36Sopenharmony_ci * @dir: Directory to add 67962306a36Sopenharmony_ci */ 68062306a36Sopenharmony_ciint tb_property_add_dir(struct tb_property_dir *parent, const char *key, 68162306a36Sopenharmony_ci struct tb_property_dir *dir) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci struct tb_property *property; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (!tb_property_key_valid(key)) 68662306a36Sopenharmony_ci return -EINVAL; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci property = tb_property_alloc(key, TB_PROPERTY_TYPE_DIRECTORY); 68962306a36Sopenharmony_ci if (!property) 69062306a36Sopenharmony_ci return -ENOMEM; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci property->value.dir = dir; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci list_add_tail(&property->list, &parent->properties); 69562306a36Sopenharmony_ci return 0; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_add_dir); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci/** 70062306a36Sopenharmony_ci * tb_property_remove() - Removes property from a parent directory 70162306a36Sopenharmony_ci * @property: Property to remove 70262306a36Sopenharmony_ci * 70362306a36Sopenharmony_ci * Note memory for @property is released as well so it is not allowed to 70462306a36Sopenharmony_ci * touch the object after call to this function. 70562306a36Sopenharmony_ci */ 70662306a36Sopenharmony_civoid tb_property_remove(struct tb_property *property) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci list_del(&property->list); 70962306a36Sopenharmony_ci kfree(property); 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_remove); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci/** 71462306a36Sopenharmony_ci * tb_property_find() - Find a property from a directory 71562306a36Sopenharmony_ci * @dir: Directory where the property is searched 71662306a36Sopenharmony_ci * @key: Key to look for 71762306a36Sopenharmony_ci * @type: Type of the property 71862306a36Sopenharmony_ci * 71962306a36Sopenharmony_ci * Finds and returns property from the given directory. Does not recurse 72062306a36Sopenharmony_ci * into sub-directories. Returns %NULL if the property was not found. 72162306a36Sopenharmony_ci */ 72262306a36Sopenharmony_cistruct tb_property *tb_property_find(struct tb_property_dir *dir, 72362306a36Sopenharmony_ci const char *key, enum tb_property_type type) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci struct tb_property *property; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci list_for_each_entry(property, &dir->properties, list) { 72862306a36Sopenharmony_ci if (property->type == type && !strcmp(property->key, key)) 72962306a36Sopenharmony_ci return property; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci return NULL; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_find); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci/** 73762306a36Sopenharmony_ci * tb_property_get_next() - Get next property from directory 73862306a36Sopenharmony_ci * @dir: Directory holding properties 73962306a36Sopenharmony_ci * @prev: Previous property in the directory (%NULL returns the first) 74062306a36Sopenharmony_ci */ 74162306a36Sopenharmony_cistruct tb_property *tb_property_get_next(struct tb_property_dir *dir, 74262306a36Sopenharmony_ci struct tb_property *prev) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci if (prev) { 74562306a36Sopenharmony_ci if (list_is_last(&prev->list, &dir->properties)) 74662306a36Sopenharmony_ci return NULL; 74762306a36Sopenharmony_ci return list_next_entry(prev, list); 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci return list_first_entry_or_null(&dir->properties, struct tb_property, 75062306a36Sopenharmony_ci list); 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_get_next); 753