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