1fd3b339cSMika Westerberg // SPDX-License-Identifier: GPL-2.0
2cdae7c07SMika Westerberg /*
3cdae7c07SMika Westerberg * Thunderbolt XDomain property support
4cdae7c07SMika Westerberg *
5cdae7c07SMika Westerberg * Copyright (C) 2017, Intel Corporation
6cdae7c07SMika Westerberg * Authors: Michael Jamet <michael.jamet@intel.com>
7cdae7c07SMika Westerberg * Mika Westerberg <mika.westerberg@linux.intel.com>
8cdae7c07SMika Westerberg */
9cdae7c07SMika Westerberg
10cdae7c07SMika Westerberg #include <linux/err.h>
11cdae7c07SMika Westerberg #include <linux/slab.h>
12cdae7c07SMika Westerberg #include <linux/string.h>
13cdae7c07SMika Westerberg #include <linux/uuid.h>
14cdae7c07SMika Westerberg #include <linux/thunderbolt.h>
15cdae7c07SMika Westerberg
16cdae7c07SMika Westerberg struct tb_property_entry {
17cdae7c07SMika Westerberg u32 key_hi;
18cdae7c07SMika Westerberg u32 key_lo;
19cdae7c07SMika Westerberg u16 length;
20cdae7c07SMika Westerberg u8 reserved;
21cdae7c07SMika Westerberg u8 type;
22cdae7c07SMika Westerberg u32 value;
23cdae7c07SMika Westerberg };
24cdae7c07SMika Westerberg
25cdae7c07SMika Westerberg struct tb_property_rootdir_entry {
26cdae7c07SMika Westerberg u32 magic;
27cdae7c07SMika Westerberg u32 length;
28cdae7c07SMika Westerberg struct tb_property_entry entries[];
29cdae7c07SMika Westerberg };
30cdae7c07SMika Westerberg
31cdae7c07SMika Westerberg struct tb_property_dir_entry {
32cdae7c07SMika Westerberg u32 uuid[4];
33cdae7c07SMika Westerberg struct tb_property_entry entries[];
34cdae7c07SMika Westerberg };
35cdae7c07SMika Westerberg
36cdae7c07SMika Westerberg #define TB_PROPERTY_ROOTDIR_MAGIC 0x55584401
37cdae7c07SMika Westerberg
38cdae7c07SMika Westerberg static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
39cdae7c07SMika Westerberg size_t block_len, unsigned int dir_offset, size_t dir_len,
40cdae7c07SMika Westerberg bool is_root);
41cdae7c07SMika Westerberg
parse_dwdata(void * dst,const void * src,size_t dwords)42cdae7c07SMika Westerberg static inline void parse_dwdata(void *dst, const void *src, size_t dwords)
43cdae7c07SMika Westerberg {
44cdae7c07SMika Westerberg be32_to_cpu_array(dst, src, dwords);
45cdae7c07SMika Westerberg }
46cdae7c07SMika Westerberg
format_dwdata(void * dst,const void * src,size_t dwords)47cdae7c07SMika Westerberg static inline void format_dwdata(void *dst, const void *src, size_t dwords)
48cdae7c07SMika Westerberg {
49cdae7c07SMika Westerberg cpu_to_be32_array(dst, src, dwords);
50cdae7c07SMika Westerberg }
51cdae7c07SMika Westerberg
tb_property_entry_valid(const struct tb_property_entry * entry,size_t block_len)52cdae7c07SMika Westerberg static bool tb_property_entry_valid(const struct tb_property_entry *entry,
53cdae7c07SMika Westerberg size_t block_len)
54cdae7c07SMika Westerberg {
55cdae7c07SMika Westerberg switch (entry->type) {
56cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_DIRECTORY:
57cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_DATA:
58cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_TEXT:
59cdae7c07SMika Westerberg if (entry->length > block_len)
60cdae7c07SMika Westerberg return false;
61cdae7c07SMika Westerberg if (entry->value + entry->length > block_len)
62cdae7c07SMika Westerberg return false;
63cdae7c07SMika Westerberg break;
64cdae7c07SMika Westerberg
65cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_VALUE:
66cdae7c07SMika Westerberg if (entry->length != 1)
67cdae7c07SMika Westerberg return false;
68cdae7c07SMika Westerberg break;
69cdae7c07SMika Westerberg }
70cdae7c07SMika Westerberg
71cdae7c07SMika Westerberg return true;
72cdae7c07SMika Westerberg }
73cdae7c07SMika Westerberg
tb_property_key_valid(const char * key)74cdae7c07SMika Westerberg static bool tb_property_key_valid(const char *key)
75cdae7c07SMika Westerberg {
76cdae7c07SMika Westerberg return key && strlen(key) <= TB_PROPERTY_KEY_SIZE;
77cdae7c07SMika Westerberg }
78cdae7c07SMika Westerberg
79cdae7c07SMika Westerberg static struct tb_property *
tb_property_alloc(const char * key,enum tb_property_type type)80cdae7c07SMika Westerberg tb_property_alloc(const char *key, enum tb_property_type type)
81cdae7c07SMika Westerberg {
82cdae7c07SMika Westerberg struct tb_property *property;
83cdae7c07SMika Westerberg
84cdae7c07SMika Westerberg property = kzalloc(sizeof(*property), GFP_KERNEL);
85cdae7c07SMika Westerberg if (!property)
86cdae7c07SMika Westerberg return NULL;
87cdae7c07SMika Westerberg
88cdae7c07SMika Westerberg strcpy(property->key, key);
89cdae7c07SMika Westerberg property->type = type;
90cdae7c07SMika Westerberg INIT_LIST_HEAD(&property->list);
91cdae7c07SMika Westerberg
92cdae7c07SMika Westerberg return property;
93cdae7c07SMika Westerberg }
94cdae7c07SMika Westerberg
tb_property_parse(const u32 * block,size_t block_len,const struct tb_property_entry * entry)95cdae7c07SMika Westerberg static struct tb_property *tb_property_parse(const u32 *block, size_t block_len,
96cdae7c07SMika Westerberg const struct tb_property_entry *entry)
97cdae7c07SMika Westerberg {
98cdae7c07SMika Westerberg char key[TB_PROPERTY_KEY_SIZE + 1];
99cdae7c07SMika Westerberg struct tb_property *property;
100cdae7c07SMika Westerberg struct tb_property_dir *dir;
101cdae7c07SMika Westerberg
102cdae7c07SMika Westerberg if (!tb_property_entry_valid(entry, block_len))
103cdae7c07SMika Westerberg return NULL;
104cdae7c07SMika Westerberg
105cdae7c07SMika Westerberg parse_dwdata(key, entry, 2);
106cdae7c07SMika Westerberg key[TB_PROPERTY_KEY_SIZE] = '\0';
107cdae7c07SMika Westerberg
108cdae7c07SMika Westerberg property = tb_property_alloc(key, entry->type);
109cdae7c07SMika Westerberg if (!property)
110cdae7c07SMika Westerberg return NULL;
111cdae7c07SMika Westerberg
112cdae7c07SMika Westerberg property->length = entry->length;
113cdae7c07SMika Westerberg
114cdae7c07SMika Westerberg switch (property->type) {
115cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_DIRECTORY:
116cdae7c07SMika Westerberg dir = __tb_property_parse_dir(block, block_len, entry->value,
117cdae7c07SMika Westerberg entry->length, false);
118cdae7c07SMika Westerberg if (!dir) {
119cdae7c07SMika Westerberg kfree(property);
120cdae7c07SMika Westerberg return NULL;
121cdae7c07SMika Westerberg }
122cdae7c07SMika Westerberg property->value.dir = dir;
123cdae7c07SMika Westerberg break;
124cdae7c07SMika Westerberg
125cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_DATA:
126cdae7c07SMika Westerberg property->value.data = kcalloc(property->length, sizeof(u32),
127cdae7c07SMika Westerberg GFP_KERNEL);
128cdae7c07SMika Westerberg if (!property->value.data) {
129cdae7c07SMika Westerberg kfree(property);
130cdae7c07SMika Westerberg return NULL;
131cdae7c07SMika Westerberg }
132cdae7c07SMika Westerberg parse_dwdata(property->value.data, block + entry->value,
133cdae7c07SMika Westerberg entry->length);
134cdae7c07SMika Westerberg break;
135cdae7c07SMika Westerberg
136cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_TEXT:
137cdae7c07SMika Westerberg property->value.text = kcalloc(property->length, sizeof(u32),
138cdae7c07SMika Westerberg GFP_KERNEL);
139cdae7c07SMika Westerberg if (!property->value.text) {
140cdae7c07SMika Westerberg kfree(property);
141cdae7c07SMika Westerberg return NULL;
142cdae7c07SMika Westerberg }
143cdae7c07SMika Westerberg parse_dwdata(property->value.text, block + entry->value,
144cdae7c07SMika Westerberg entry->length);
145cdae7c07SMika Westerberg /* Force null termination */
146cdae7c07SMika Westerberg property->value.text[property->length * 4 - 1] = '\0';
147cdae7c07SMika Westerberg break;
148cdae7c07SMika Westerberg
149cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_VALUE:
150cdae7c07SMika Westerberg property->value.immediate = entry->value;
151cdae7c07SMika Westerberg break;
152cdae7c07SMika Westerberg
153cdae7c07SMika Westerberg default:
154cdae7c07SMika Westerberg property->type = TB_PROPERTY_TYPE_UNKNOWN;
155cdae7c07SMika Westerberg break;
156cdae7c07SMika Westerberg }
157cdae7c07SMika Westerberg
158cdae7c07SMika Westerberg return property;
159cdae7c07SMika Westerberg }
160cdae7c07SMika Westerberg
__tb_property_parse_dir(const u32 * block,size_t block_len,unsigned int dir_offset,size_t dir_len,bool is_root)161cdae7c07SMika Westerberg static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
162cdae7c07SMika Westerberg size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root)
163cdae7c07SMika Westerberg {
164cdae7c07SMika Westerberg const struct tb_property_entry *entries;
165cdae7c07SMika Westerberg size_t i, content_len, nentries;
166cdae7c07SMika Westerberg unsigned int content_offset;
167cdae7c07SMika Westerberg struct tb_property_dir *dir;
168cdae7c07SMika Westerberg
169cdae7c07SMika Westerberg dir = kzalloc(sizeof(*dir), GFP_KERNEL);
170cdae7c07SMika Westerberg if (!dir)
171cdae7c07SMika Westerberg return NULL;
172cdae7c07SMika Westerberg
173cdae7c07SMika Westerberg if (is_root) {
174cdae7c07SMika Westerberg content_offset = dir_offset + 2;
175cdae7c07SMika Westerberg content_len = dir_len;
176cdae7c07SMika Westerberg } else {
177cdae7c07SMika Westerberg dir->uuid = kmemdup(&block[dir_offset], sizeof(*dir->uuid),
178cdae7c07SMika Westerberg GFP_KERNEL);
179e4dfdd58SKangjie Lu if (!dir->uuid) {
180e4dfdd58SKangjie Lu tb_property_free_dir(dir);
181e4dfdd58SKangjie Lu return NULL;
182e4dfdd58SKangjie Lu }
183cdae7c07SMika Westerberg content_offset = dir_offset + 4;
184cdae7c07SMika Westerberg content_len = dir_len - 4; /* Length includes UUID */
185cdae7c07SMika Westerberg }
186cdae7c07SMika Westerberg
187cdae7c07SMika Westerberg entries = (const struct tb_property_entry *)&block[content_offset];
188cdae7c07SMika Westerberg nentries = content_len / (sizeof(*entries) / 4);
189cdae7c07SMika Westerberg
190cdae7c07SMika Westerberg INIT_LIST_HEAD(&dir->properties);
191cdae7c07SMika Westerberg
192cdae7c07SMika Westerberg for (i = 0; i < nentries; i++) {
193cdae7c07SMika Westerberg struct tb_property *property;
194cdae7c07SMika Westerberg
195cdae7c07SMika Westerberg property = tb_property_parse(block, block_len, &entries[i]);
196cdae7c07SMika Westerberg if (!property) {
197cdae7c07SMika Westerberg tb_property_free_dir(dir);
198cdae7c07SMika Westerberg return NULL;
199cdae7c07SMika Westerberg }
200cdae7c07SMika Westerberg
201cdae7c07SMika Westerberg list_add_tail(&property->list, &dir->properties);
202cdae7c07SMika Westerberg }
203cdae7c07SMika Westerberg
204cdae7c07SMika Westerberg return dir;
205cdae7c07SMika Westerberg }
206cdae7c07SMika Westerberg
207cdae7c07SMika Westerberg /**
208cdae7c07SMika Westerberg * tb_property_parse_dir() - Parses properties from given property block
209cdae7c07SMika Westerberg * @block: Property block to parse
210cdae7c07SMika Westerberg * @block_len: Number of dword elements in the property block
211cdae7c07SMika Westerberg *
212cdae7c07SMika Westerberg * This function parses the XDomain properties data block into format that
213cdae7c07SMika Westerberg * can be traversed using the helper functions provided by this module.
214cdae7c07SMika Westerberg * Upon success returns the parsed directory. In case of error returns
215cdae7c07SMika Westerberg * %NULL. The resulting &struct tb_property_dir needs to be released by
216cdae7c07SMika Westerberg * calling tb_property_free_dir() when not needed anymore.
217cdae7c07SMika Westerberg *
218cdae7c07SMika Westerberg * The @block is expected to be root directory.
219cdae7c07SMika Westerberg */
tb_property_parse_dir(const u32 * block,size_t block_len)220cdae7c07SMika Westerberg struct tb_property_dir *tb_property_parse_dir(const u32 *block,
221cdae7c07SMika Westerberg size_t block_len)
222cdae7c07SMika Westerberg {
223cdae7c07SMika Westerberg const struct tb_property_rootdir_entry *rootdir =
224cdae7c07SMika Westerberg (const struct tb_property_rootdir_entry *)block;
225cdae7c07SMika Westerberg
226cdae7c07SMika Westerberg if (rootdir->magic != TB_PROPERTY_ROOTDIR_MAGIC)
227cdae7c07SMika Westerberg return NULL;
228cdae7c07SMika Westerberg if (rootdir->length > block_len)
229cdae7c07SMika Westerberg return NULL;
230cdae7c07SMika Westerberg
231cdae7c07SMika Westerberg return __tb_property_parse_dir(block, block_len, 0, rootdir->length,
232cdae7c07SMika Westerberg true);
233cdae7c07SMika Westerberg }
234cdae7c07SMika Westerberg
235cdae7c07SMika Westerberg /**
236cdae7c07SMika Westerberg * tb_property_create_dir() - Creates new property directory
237cdae7c07SMika Westerberg * @uuid: UUID used to identify the particular directory
238cdae7c07SMika Westerberg *
239cdae7c07SMika Westerberg * Creates new, empty property directory. If @uuid is %NULL then the
240cdae7c07SMika Westerberg * directory is assumed to be root directory.
241cdae7c07SMika Westerberg */
tb_property_create_dir(const uuid_t * uuid)242cdae7c07SMika Westerberg struct tb_property_dir *tb_property_create_dir(const uuid_t *uuid)
243cdae7c07SMika Westerberg {
244cdae7c07SMika Westerberg struct tb_property_dir *dir;
245cdae7c07SMika Westerberg
246cdae7c07SMika Westerberg dir = kzalloc(sizeof(*dir), GFP_KERNEL);
247cdae7c07SMika Westerberg if (!dir)
248cdae7c07SMika Westerberg return NULL;
249cdae7c07SMika Westerberg
250cdae7c07SMika Westerberg INIT_LIST_HEAD(&dir->properties);
251cdae7c07SMika Westerberg if (uuid) {
252cdae7c07SMika Westerberg dir->uuid = kmemdup(uuid, sizeof(*dir->uuid), GFP_KERNEL);
253cdae7c07SMika Westerberg if (!dir->uuid) {
254cdae7c07SMika Westerberg kfree(dir);
255cdae7c07SMika Westerberg return NULL;
256cdae7c07SMika Westerberg }
257cdae7c07SMika Westerberg }
258cdae7c07SMika Westerberg
259cdae7c07SMika Westerberg return dir;
260cdae7c07SMika Westerberg }
261cdae7c07SMika Westerberg EXPORT_SYMBOL_GPL(tb_property_create_dir);
262cdae7c07SMika Westerberg
tb_property_free(struct tb_property * property)263cdae7c07SMika Westerberg static void tb_property_free(struct tb_property *property)
264cdae7c07SMika Westerberg {
265cdae7c07SMika Westerberg switch (property->type) {
266cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_DIRECTORY:
267cdae7c07SMika Westerberg tb_property_free_dir(property->value.dir);
268cdae7c07SMika Westerberg break;
269cdae7c07SMika Westerberg
270cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_DATA:
271cdae7c07SMika Westerberg kfree(property->value.data);
272cdae7c07SMika Westerberg break;
273cdae7c07SMika Westerberg
274cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_TEXT:
275cdae7c07SMika Westerberg kfree(property->value.text);
276cdae7c07SMika Westerberg break;
277cdae7c07SMika Westerberg
278cdae7c07SMika Westerberg default:
279cdae7c07SMika Westerberg break;
280cdae7c07SMika Westerberg }
281cdae7c07SMika Westerberg
282cdae7c07SMika Westerberg kfree(property);
283cdae7c07SMika Westerberg }
284cdae7c07SMika Westerberg
285cdae7c07SMika Westerberg /**
286cdae7c07SMika Westerberg * tb_property_free_dir() - Release memory allocated for property directory
287cdae7c07SMika Westerberg * @dir: Directory to release
288cdae7c07SMika Westerberg *
289cdae7c07SMika Westerberg * This will release all the memory the directory occupies including all
290cdae7c07SMika Westerberg * descendants. It is OK to pass %NULL @dir, then the function does
291cdae7c07SMika Westerberg * nothing.
292cdae7c07SMika Westerberg */
tb_property_free_dir(struct tb_property_dir * dir)293cdae7c07SMika Westerberg void tb_property_free_dir(struct tb_property_dir *dir)
294cdae7c07SMika Westerberg {
295cdae7c07SMika Westerberg struct tb_property *property, *tmp;
296cdae7c07SMika Westerberg
297cdae7c07SMika Westerberg if (!dir)
298cdae7c07SMika Westerberg return;
299cdae7c07SMika Westerberg
300cdae7c07SMika Westerberg list_for_each_entry_safe(property, tmp, &dir->properties, list) {
301cdae7c07SMika Westerberg list_del(&property->list);
302cdae7c07SMika Westerberg tb_property_free(property);
303cdae7c07SMika Westerberg }
304cdae7c07SMika Westerberg kfree(dir->uuid);
305cdae7c07SMika Westerberg kfree(dir);
306cdae7c07SMika Westerberg }
307cdae7c07SMika Westerberg EXPORT_SYMBOL_GPL(tb_property_free_dir);
308cdae7c07SMika Westerberg
tb_property_dir_length(const struct tb_property_dir * dir,bool recurse,size_t * data_len)309cdae7c07SMika Westerberg static size_t tb_property_dir_length(const struct tb_property_dir *dir,
310cdae7c07SMika Westerberg bool recurse, size_t *data_len)
311cdae7c07SMika Westerberg {
312cdae7c07SMika Westerberg const struct tb_property *property;
313cdae7c07SMika Westerberg size_t len = 0;
314cdae7c07SMika Westerberg
315cdae7c07SMika Westerberg if (dir->uuid)
316cdae7c07SMika Westerberg len += sizeof(*dir->uuid) / 4;
317cdae7c07SMika Westerberg else
318cdae7c07SMika Westerberg len += sizeof(struct tb_property_rootdir_entry) / 4;
319cdae7c07SMika Westerberg
320cdae7c07SMika Westerberg list_for_each_entry(property, &dir->properties, list) {
321cdae7c07SMika Westerberg len += sizeof(struct tb_property_entry) / 4;
322cdae7c07SMika Westerberg
323cdae7c07SMika Westerberg switch (property->type) {
324cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_DIRECTORY:
325cdae7c07SMika Westerberg if (recurse) {
326cdae7c07SMika Westerberg len += tb_property_dir_length(
327cdae7c07SMika Westerberg property->value.dir, recurse, data_len);
328cdae7c07SMika Westerberg }
329cdae7c07SMika Westerberg /* Reserve dword padding after each directory */
330cdae7c07SMika Westerberg if (data_len)
331cdae7c07SMika Westerberg *data_len += 1;
332cdae7c07SMika Westerberg break;
333cdae7c07SMika Westerberg
334cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_DATA:
335cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_TEXT:
336cdae7c07SMika Westerberg if (data_len)
337cdae7c07SMika Westerberg *data_len += property->length;
338cdae7c07SMika Westerberg break;
339cdae7c07SMika Westerberg
340cdae7c07SMika Westerberg default:
341cdae7c07SMika Westerberg break;
342cdae7c07SMika Westerberg }
343cdae7c07SMika Westerberg }
344cdae7c07SMika Westerberg
345cdae7c07SMika Westerberg return len;
346cdae7c07SMika Westerberg }
347cdae7c07SMika Westerberg
__tb_property_format_dir(const struct tb_property_dir * dir,u32 * block,unsigned int start_offset,size_t block_len)348cdae7c07SMika Westerberg static ssize_t __tb_property_format_dir(const struct tb_property_dir *dir,
349cdae7c07SMika Westerberg u32 *block, unsigned int start_offset, size_t block_len)
350cdae7c07SMika Westerberg {
351cdae7c07SMika Westerberg unsigned int data_offset, dir_end;
352cdae7c07SMika Westerberg const struct tb_property *property;
353cdae7c07SMika Westerberg struct tb_property_entry *entry;
354cdae7c07SMika Westerberg size_t dir_len, data_len = 0;
355cdae7c07SMika Westerberg int ret;
356cdae7c07SMika Westerberg
357cdae7c07SMika Westerberg /*
358cdae7c07SMika Westerberg * The structure of property block looks like following. Leaf
359cdae7c07SMika Westerberg * data/text is included right after the directory and each
360cdae7c07SMika Westerberg * directory follows each other (even nested ones).
361cdae7c07SMika Westerberg *
362cdae7c07SMika Westerberg * +----------+ <-- start_offset
363cdae7c07SMika Westerberg * | header | <-- root directory header
364cdae7c07SMika Westerberg * +----------+ ---
365cdae7c07SMika Westerberg * | entry 0 | -^--------------------.
366cdae7c07SMika Westerberg * +----------+ | |
367cdae7c07SMika Westerberg * | entry 1 | -|--------------------|--.
368cdae7c07SMika Westerberg * +----------+ | | |
369cdae7c07SMika Westerberg * | entry 2 | -|-----------------. | |
370cdae7c07SMika Westerberg * +----------+ | | | |
371cdae7c07SMika Westerberg * : : | dir_len | | |
372cdae7c07SMika Westerberg * . . | | | |
373cdae7c07SMika Westerberg * : : | | | |
374cdae7c07SMika Westerberg * +----------+ | | | |
375cdae7c07SMika Westerberg * | entry n | v | | |
376cdae7c07SMika Westerberg * +----------+ <-- data_offset | | |
377cdae7c07SMika Westerberg * | data 0 | <------------------|--' |
378cdae7c07SMika Westerberg * +----------+ | |
379cdae7c07SMika Westerberg * | data 1 | <------------------|-----'
380cdae7c07SMika Westerberg * +----------+ |
381cdae7c07SMika Westerberg * | 00000000 | padding |
382cdae7c07SMika Westerberg * +----------+ <-- dir_end <------'
383cdae7c07SMika Westerberg * | UUID | <-- directory UUID (child directory)
384cdae7c07SMika Westerberg * +----------+
385cdae7c07SMika Westerberg * | entry 0 |
386cdae7c07SMika Westerberg * +----------+
387cdae7c07SMika Westerberg * | entry 1 |
388cdae7c07SMika Westerberg * +----------+
389cdae7c07SMika Westerberg * : :
390cdae7c07SMika Westerberg * . .
391cdae7c07SMika Westerberg * : :
392cdae7c07SMika Westerberg * +----------+
393cdae7c07SMika Westerberg * | entry n |
394cdae7c07SMika Westerberg * +----------+
395cdae7c07SMika Westerberg * | data 0 |
396cdae7c07SMika Westerberg * +----------+
397cdae7c07SMika Westerberg *
398cdae7c07SMika Westerberg * We use dir_end to hold pointer to the end of the directory. It
399cdae7c07SMika Westerberg * will increase as we add directories and each directory should be
400cdae7c07SMika Westerberg * added starting from previous dir_end.
401cdae7c07SMika Westerberg */
402cdae7c07SMika Westerberg dir_len = tb_property_dir_length(dir, false, &data_len);
403cdae7c07SMika Westerberg data_offset = start_offset + dir_len;
404cdae7c07SMika Westerberg dir_end = start_offset + data_len + dir_len;
405cdae7c07SMika Westerberg
406cdae7c07SMika Westerberg if (data_offset > dir_end)
407cdae7c07SMika Westerberg return -EINVAL;
408cdae7c07SMika Westerberg if (dir_end > block_len)
409cdae7c07SMika Westerberg return -EINVAL;
410cdae7c07SMika Westerberg
411cdae7c07SMika Westerberg /* Write headers first */
412cdae7c07SMika Westerberg if (dir->uuid) {
413cdae7c07SMika Westerberg struct tb_property_dir_entry *pe;
414cdae7c07SMika Westerberg
415cdae7c07SMika Westerberg pe = (struct tb_property_dir_entry *)&block[start_offset];
416cdae7c07SMika Westerberg memcpy(pe->uuid, dir->uuid, sizeof(pe->uuid));
417cdae7c07SMika Westerberg entry = pe->entries;
418cdae7c07SMika Westerberg } else {
419cdae7c07SMika Westerberg struct tb_property_rootdir_entry *re;
420cdae7c07SMika Westerberg
421cdae7c07SMika Westerberg re = (struct tb_property_rootdir_entry *)&block[start_offset];
422cdae7c07SMika Westerberg re->magic = TB_PROPERTY_ROOTDIR_MAGIC;
423cdae7c07SMika Westerberg re->length = dir_len - sizeof(*re) / 4;
424cdae7c07SMika Westerberg entry = re->entries;
425cdae7c07SMika Westerberg }
426cdae7c07SMika Westerberg
427cdae7c07SMika Westerberg list_for_each_entry(property, &dir->properties, list) {
428cdae7c07SMika Westerberg const struct tb_property_dir *child;
429cdae7c07SMika Westerberg
430cdae7c07SMika Westerberg format_dwdata(entry, property->key, 2);
431cdae7c07SMika Westerberg entry->type = property->type;
432cdae7c07SMika Westerberg
433cdae7c07SMika Westerberg switch (property->type) {
434cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_DIRECTORY:
435cdae7c07SMika Westerberg child = property->value.dir;
436cdae7c07SMika Westerberg ret = __tb_property_format_dir(child, block, dir_end,
437cdae7c07SMika Westerberg block_len);
438cdae7c07SMika Westerberg if (ret < 0)
439cdae7c07SMika Westerberg return ret;
440cdae7c07SMika Westerberg entry->length = tb_property_dir_length(child, false,
441cdae7c07SMika Westerberg NULL);
442cdae7c07SMika Westerberg entry->value = dir_end;
443cdae7c07SMika Westerberg dir_end = ret;
444cdae7c07SMika Westerberg break;
445cdae7c07SMika Westerberg
446cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_DATA:
447cdae7c07SMika Westerberg format_dwdata(&block[data_offset], property->value.data,
448cdae7c07SMika Westerberg property->length);
449cdae7c07SMika Westerberg entry->length = property->length;
450cdae7c07SMika Westerberg entry->value = data_offset;
451cdae7c07SMika Westerberg data_offset += entry->length;
452cdae7c07SMika Westerberg break;
453cdae7c07SMika Westerberg
454cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_TEXT:
455cdae7c07SMika Westerberg format_dwdata(&block[data_offset], property->value.text,
456cdae7c07SMika Westerberg property->length);
457cdae7c07SMika Westerberg entry->length = property->length;
458cdae7c07SMika Westerberg entry->value = data_offset;
459cdae7c07SMika Westerberg data_offset += entry->length;
460cdae7c07SMika Westerberg break;
461cdae7c07SMika Westerberg
462cdae7c07SMika Westerberg case TB_PROPERTY_TYPE_VALUE:
463cdae7c07SMika Westerberg entry->length = property->length;
464cdae7c07SMika Westerberg entry->value = property->value.immediate;
465cdae7c07SMika Westerberg break;
466cdae7c07SMika Westerberg
467cdae7c07SMika Westerberg default:
468cdae7c07SMika Westerberg break;
469cdae7c07SMika Westerberg }
470cdae7c07SMika Westerberg
471cdae7c07SMika Westerberg entry++;
472cdae7c07SMika Westerberg }
473cdae7c07SMika Westerberg
474cdae7c07SMika Westerberg return dir_end;
475cdae7c07SMika Westerberg }
476cdae7c07SMika Westerberg
477cdae7c07SMika Westerberg /**
478cdae7c07SMika Westerberg * tb_property_format_dir() - Formats directory to the packed XDomain format
479cdae7c07SMika Westerberg * @dir: Directory to format
480cdae7c07SMika Westerberg * @block: Property block where the packed data is placed
481cdae7c07SMika Westerberg * @block_len: Length of the property block
482cdae7c07SMika Westerberg *
483cdae7c07SMika Westerberg * This function formats the directory to the packed format that can be
484cdae7c07SMika Westerberg * then send over the thunderbolt fabric to receiving host. Returns %0 in
485cdae7c07SMika Westerberg * case of success and negative errno on faulure. Passing %NULL in @block
486cdae7c07SMika Westerberg * returns number of entries the block takes.
487cdae7c07SMika Westerberg */
tb_property_format_dir(const struct tb_property_dir * dir,u32 * block,size_t block_len)488cdae7c07SMika Westerberg ssize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block,
489cdae7c07SMika Westerberg size_t block_len)
490cdae7c07SMika Westerberg {
491cdae7c07SMika Westerberg ssize_t ret;
492cdae7c07SMika Westerberg
493cdae7c07SMika Westerberg if (!block) {
494cdae7c07SMika Westerberg size_t dir_len, data_len = 0;
495cdae7c07SMika Westerberg
496cdae7c07SMika Westerberg dir_len = tb_property_dir_length(dir, true, &data_len);
497cdae7c07SMika Westerberg return dir_len + data_len;
498cdae7c07SMika Westerberg }
499cdae7c07SMika Westerberg
500cdae7c07SMika Westerberg ret = __tb_property_format_dir(dir, block, 0, block_len);
501cdae7c07SMika Westerberg return ret < 0 ? ret : 0;
502cdae7c07SMika Westerberg }
503cdae7c07SMika Westerberg
504cdae7c07SMika Westerberg /**
505*7d3084c0SMika Westerberg * tb_property_copy_dir() - Take a deep copy of directory
506*7d3084c0SMika Westerberg * @dir: Directory to copy
507*7d3084c0SMika Westerberg *
508*7d3084c0SMika Westerberg * This function takes a deep copy of @dir and returns back the copy. In
509*7d3084c0SMika Westerberg * case of error returns %NULL. The resulting directory needs to be
510*7d3084c0SMika Westerberg * released by calling tb_property_free_dir().
511*7d3084c0SMika Westerberg */
tb_property_copy_dir(const struct tb_property_dir * dir)512*7d3084c0SMika Westerberg struct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir)
513*7d3084c0SMika Westerberg {
514*7d3084c0SMika Westerberg struct tb_property *property, *p = NULL;
515*7d3084c0SMika Westerberg struct tb_property_dir *d;
516*7d3084c0SMika Westerberg
517*7d3084c0SMika Westerberg if (!dir)
518*7d3084c0SMika Westerberg return NULL;
519*7d3084c0SMika Westerberg
520*7d3084c0SMika Westerberg d = tb_property_create_dir(dir->uuid);
521*7d3084c0SMika Westerberg if (!d)
522*7d3084c0SMika Westerberg return NULL;
523*7d3084c0SMika Westerberg
524*7d3084c0SMika Westerberg list_for_each_entry(property, &dir->properties, list) {
525*7d3084c0SMika Westerberg struct tb_property *p;
526*7d3084c0SMika Westerberg
527*7d3084c0SMika Westerberg p = tb_property_alloc(property->key, property->type);
528*7d3084c0SMika Westerberg if (!p)
529*7d3084c0SMika Westerberg goto err_free;
530*7d3084c0SMika Westerberg
531*7d3084c0SMika Westerberg p->length = property->length;
532*7d3084c0SMika Westerberg
533*7d3084c0SMika Westerberg switch (property->type) {
534*7d3084c0SMika Westerberg case TB_PROPERTY_TYPE_DIRECTORY:
535*7d3084c0SMika Westerberg p->value.dir = tb_property_copy_dir(property->value.dir);
536*7d3084c0SMika Westerberg if (!p->value.dir)
537*7d3084c0SMika Westerberg goto err_free;
538*7d3084c0SMika Westerberg break;
539*7d3084c0SMika Westerberg
540*7d3084c0SMika Westerberg case TB_PROPERTY_TYPE_DATA:
541*7d3084c0SMika Westerberg p->value.data = kmemdup(property->value.data,
542*7d3084c0SMika Westerberg property->length * 4,
543*7d3084c0SMika Westerberg GFP_KERNEL);
544*7d3084c0SMika Westerberg if (!p->value.data)
545*7d3084c0SMika Westerberg goto err_free;
546*7d3084c0SMika Westerberg break;
547*7d3084c0SMika Westerberg
548*7d3084c0SMika Westerberg case TB_PROPERTY_TYPE_TEXT:
549*7d3084c0SMika Westerberg p->value.text = kzalloc(p->length * 4, GFP_KERNEL);
550*7d3084c0SMika Westerberg if (!p->value.text)
551*7d3084c0SMika Westerberg goto err_free;
552*7d3084c0SMika Westerberg strcpy(p->value.text, property->value.text);
553*7d3084c0SMika Westerberg break;
554*7d3084c0SMika Westerberg
555*7d3084c0SMika Westerberg case TB_PROPERTY_TYPE_VALUE:
556*7d3084c0SMika Westerberg p->value.immediate = property->value.immediate;
557*7d3084c0SMika Westerberg break;
558*7d3084c0SMika Westerberg
559*7d3084c0SMika Westerberg default:
560*7d3084c0SMika Westerberg break;
561*7d3084c0SMika Westerberg }
562*7d3084c0SMika Westerberg
563*7d3084c0SMika Westerberg list_add_tail(&p->list, &d->properties);
564*7d3084c0SMika Westerberg }
565*7d3084c0SMika Westerberg
566*7d3084c0SMika Westerberg return d;
567*7d3084c0SMika Westerberg
568*7d3084c0SMika Westerberg err_free:
569*7d3084c0SMika Westerberg kfree(p);
570*7d3084c0SMika Westerberg tb_property_free_dir(d);
571*7d3084c0SMika Westerberg
572*7d3084c0SMika Westerberg return NULL;
573*7d3084c0SMika Westerberg }
574*7d3084c0SMika Westerberg
575*7d3084c0SMika Westerberg /**
576cdae7c07SMika Westerberg * tb_property_add_immediate() - Add immediate property to directory
577cdae7c07SMika Westerberg * @parent: Directory to add the property
578cdae7c07SMika Westerberg * @key: Key for the property
579cdae7c07SMika Westerberg * @value: Immediate value to store with the property
580cdae7c07SMika Westerberg */
tb_property_add_immediate(struct tb_property_dir * parent,const char * key,u32 value)581cdae7c07SMika Westerberg int tb_property_add_immediate(struct tb_property_dir *parent, const char *key,
582cdae7c07SMika Westerberg u32 value)
583cdae7c07SMika Westerberg {
584cdae7c07SMika Westerberg struct tb_property *property;
585cdae7c07SMika Westerberg
586cdae7c07SMika Westerberg if (!tb_property_key_valid(key))
587cdae7c07SMika Westerberg return -EINVAL;
588cdae7c07SMika Westerberg
589cdae7c07SMika Westerberg property = tb_property_alloc(key, TB_PROPERTY_TYPE_VALUE);
590cdae7c07SMika Westerberg if (!property)
591cdae7c07SMika Westerberg return -ENOMEM;
592cdae7c07SMika Westerberg
593cdae7c07SMika Westerberg property->length = 1;
594cdae7c07SMika Westerberg property->value.immediate = value;
595cdae7c07SMika Westerberg
596cdae7c07SMika Westerberg list_add_tail(&property->list, &parent->properties);
597cdae7c07SMika Westerberg return 0;
598cdae7c07SMika Westerberg }
599cdae7c07SMika Westerberg EXPORT_SYMBOL_GPL(tb_property_add_immediate);
600cdae7c07SMika Westerberg
601cdae7c07SMika Westerberg /**
602cdae7c07SMika Westerberg * tb_property_add_data() - Adds arbitrary data property to directory
603cdae7c07SMika Westerberg * @parent: Directory to add the property
604cdae7c07SMika Westerberg * @key: Key for the property
605cdae7c07SMika Westerberg * @buf: Data buffer to add
606cdae7c07SMika Westerberg * @buflen: Number of bytes in the data buffer
607cdae7c07SMika Westerberg *
608cdae7c07SMika Westerberg * Function takes a copy of @buf and adds it to the directory.
609cdae7c07SMika Westerberg */
tb_property_add_data(struct tb_property_dir * parent,const char * key,const void * buf,size_t buflen)610cdae7c07SMika Westerberg int tb_property_add_data(struct tb_property_dir *parent, const char *key,
611cdae7c07SMika Westerberg const void *buf, size_t buflen)
612cdae7c07SMika Westerberg {
613cdae7c07SMika Westerberg /* Need to pad to dword boundary */
614cdae7c07SMika Westerberg size_t size = round_up(buflen, 4);
615cdae7c07SMika Westerberg struct tb_property *property;
616cdae7c07SMika Westerberg
617cdae7c07SMika Westerberg if (!tb_property_key_valid(key))
618cdae7c07SMika Westerberg return -EINVAL;
619cdae7c07SMika Westerberg
620cdae7c07SMika Westerberg property = tb_property_alloc(key, TB_PROPERTY_TYPE_DATA);
621cdae7c07SMika Westerberg if (!property)
622cdae7c07SMika Westerberg return -ENOMEM;
623cdae7c07SMika Westerberg
624cdae7c07SMika Westerberg property->length = size / 4;
625cdae7c07SMika Westerberg property->value.data = kzalloc(size, GFP_KERNEL);
626106204b5SKangjie Lu if (!property->value.data) {
627106204b5SKangjie Lu kfree(property);
628106204b5SKangjie Lu return -ENOMEM;
629106204b5SKangjie Lu }
630106204b5SKangjie Lu
631cdae7c07SMika Westerberg memcpy(property->value.data, buf, buflen);
632cdae7c07SMika Westerberg
633cdae7c07SMika Westerberg list_add_tail(&property->list, &parent->properties);
634cdae7c07SMika Westerberg return 0;
635cdae7c07SMika Westerberg }
636cdae7c07SMika Westerberg EXPORT_SYMBOL_GPL(tb_property_add_data);
637cdae7c07SMika Westerberg
638cdae7c07SMika Westerberg /**
639cdae7c07SMika Westerberg * tb_property_add_text() - Adds string property to directory
640cdae7c07SMika Westerberg * @parent: Directory to add the property
641cdae7c07SMika Westerberg * @key: Key for the property
642cdae7c07SMika Westerberg * @text: String to add
643cdae7c07SMika Westerberg *
644cdae7c07SMika Westerberg * Function takes a copy of @text and adds it to the directory.
645cdae7c07SMika Westerberg */
tb_property_add_text(struct tb_property_dir * parent,const char * key,const char * text)646cdae7c07SMika Westerberg int tb_property_add_text(struct tb_property_dir *parent, const char *key,
647cdae7c07SMika Westerberg const char *text)
648cdae7c07SMika Westerberg {
649cdae7c07SMika Westerberg /* Need to pad to dword boundary */
650cdae7c07SMika Westerberg size_t size = round_up(strlen(text) + 1, 4);
651cdae7c07SMika Westerberg struct tb_property *property;
652cdae7c07SMika Westerberg
653cdae7c07SMika Westerberg if (!tb_property_key_valid(key))
654cdae7c07SMika Westerberg return -EINVAL;
655cdae7c07SMika Westerberg
656cdae7c07SMika Westerberg property = tb_property_alloc(key, TB_PROPERTY_TYPE_TEXT);
657cdae7c07SMika Westerberg if (!property)
658cdae7c07SMika Westerberg return -ENOMEM;
659cdae7c07SMika Westerberg
660cdae7c07SMika Westerberg property->length = size / 4;
6616183d5a5SKangjie Lu property->value.text = kzalloc(size, GFP_KERNEL);
6626183d5a5SKangjie Lu if (!property->value.text) {
6636183d5a5SKangjie Lu kfree(property);
6646183d5a5SKangjie Lu return -ENOMEM;
6656183d5a5SKangjie Lu }
6666183d5a5SKangjie Lu
667cdae7c07SMika Westerberg strcpy(property->value.text, text);
668cdae7c07SMika Westerberg
669cdae7c07SMika Westerberg list_add_tail(&property->list, &parent->properties);
670cdae7c07SMika Westerberg return 0;
671cdae7c07SMika Westerberg }
672cdae7c07SMika Westerberg EXPORT_SYMBOL_GPL(tb_property_add_text);
673cdae7c07SMika Westerberg
674cdae7c07SMika Westerberg /**
675cdae7c07SMika Westerberg * tb_property_add_dir() - Adds a directory to the parent directory
676cdae7c07SMika Westerberg * @parent: Directory to add the property
677cdae7c07SMika Westerberg * @key: Key for the property
678cdae7c07SMika Westerberg * @dir: Directory to add
679cdae7c07SMika Westerberg */
tb_property_add_dir(struct tb_property_dir * parent,const char * key,struct tb_property_dir * dir)680cdae7c07SMika Westerberg int tb_property_add_dir(struct tb_property_dir *parent, const char *key,
681cdae7c07SMika Westerberg struct tb_property_dir *dir)
682cdae7c07SMika Westerberg {
683cdae7c07SMika Westerberg struct tb_property *property;
684cdae7c07SMika Westerberg
685cdae7c07SMika Westerberg if (!tb_property_key_valid(key))
686cdae7c07SMika Westerberg return -EINVAL;
687cdae7c07SMika Westerberg
688cdae7c07SMika Westerberg property = tb_property_alloc(key, TB_PROPERTY_TYPE_DIRECTORY);
689cdae7c07SMika Westerberg if (!property)
690cdae7c07SMika Westerberg return -ENOMEM;
691cdae7c07SMika Westerberg
692cdae7c07SMika Westerberg property->value.dir = dir;
693cdae7c07SMika Westerberg
694cdae7c07SMika Westerberg list_add_tail(&property->list, &parent->properties);
695cdae7c07SMika Westerberg return 0;
696cdae7c07SMika Westerberg }
697cdae7c07SMika Westerberg EXPORT_SYMBOL_GPL(tb_property_add_dir);
698cdae7c07SMika Westerberg
699cdae7c07SMika Westerberg /**
700cdae7c07SMika Westerberg * tb_property_remove() - Removes property from a parent directory
701cdae7c07SMika Westerberg * @property: Property to remove
702cdae7c07SMika Westerberg *
703cdae7c07SMika Westerberg * Note memory for @property is released as well so it is not allowed to
704cdae7c07SMika Westerberg * touch the object after call to this function.
705cdae7c07SMika Westerberg */
tb_property_remove(struct tb_property * property)706cdae7c07SMika Westerberg void tb_property_remove(struct tb_property *property)
707cdae7c07SMika Westerberg {
708cdae7c07SMika Westerberg list_del(&property->list);
709cdae7c07SMika Westerberg kfree(property);
710cdae7c07SMika Westerberg }
711cdae7c07SMika Westerberg EXPORT_SYMBOL_GPL(tb_property_remove);
712cdae7c07SMika Westerberg
713cdae7c07SMika Westerberg /**
714cdae7c07SMika Westerberg * tb_property_find() - Find a property from a directory
715cdae7c07SMika Westerberg * @dir: Directory where the property is searched
716cdae7c07SMika Westerberg * @key: Key to look for
717cdae7c07SMika Westerberg * @type: Type of the property
718cdae7c07SMika Westerberg *
719cdae7c07SMika Westerberg * Finds and returns property from the given directory. Does not recurse
720cdae7c07SMika Westerberg * into sub-directories. Returns %NULL if the property was not found.
721cdae7c07SMika Westerberg */
tb_property_find(struct tb_property_dir * dir,const char * key,enum tb_property_type type)722cdae7c07SMika Westerberg struct tb_property *tb_property_find(struct tb_property_dir *dir,
723cdae7c07SMika Westerberg const char *key, enum tb_property_type type)
724cdae7c07SMika Westerberg {
725cdae7c07SMika Westerberg struct tb_property *property;
726cdae7c07SMika Westerberg
727cdae7c07SMika Westerberg list_for_each_entry(property, &dir->properties, list) {
728cdae7c07SMika Westerberg if (property->type == type && !strcmp(property->key, key))
729cdae7c07SMika Westerberg return property;
730cdae7c07SMika Westerberg }
731cdae7c07SMika Westerberg
732cdae7c07SMika Westerberg return NULL;
733cdae7c07SMika Westerberg }
734cdae7c07SMika Westerberg EXPORT_SYMBOL_GPL(tb_property_find);
735cdae7c07SMika Westerberg
736cdae7c07SMika Westerberg /**
737cdae7c07SMika Westerberg * tb_property_get_next() - Get next property from directory
738cdae7c07SMika Westerberg * @dir: Directory holding properties
739cdae7c07SMika Westerberg * @prev: Previous property in the directory (%NULL returns the first)
740cdae7c07SMika Westerberg */
tb_property_get_next(struct tb_property_dir * dir,struct tb_property * prev)741cdae7c07SMika Westerberg struct tb_property *tb_property_get_next(struct tb_property_dir *dir,
742cdae7c07SMika Westerberg struct tb_property *prev)
743cdae7c07SMika Westerberg {
744cdae7c07SMika Westerberg if (prev) {
745cdae7c07SMika Westerberg if (list_is_last(&prev->list, &dir->properties))
746cdae7c07SMika Westerberg return NULL;
747cdae7c07SMika Westerberg return list_next_entry(prev, list);
748cdae7c07SMika Westerberg }
749cdae7c07SMika Westerberg return list_first_entry_or_null(&dir->properties, struct tb_property,
750cdae7c07SMika Westerberg list);
751cdae7c07SMika Westerberg }
752cdae7c07SMika Westerberg EXPORT_SYMBOL_GPL(tb_property_get_next);
753