From e6bff4665c595b5a4aff173848851ed49ac3bfad Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 7 Nov 2019 20:22:20 -0800 Subject: software node: replace is_array with is_inline We do not need a special flag to know if we are dealing with an array, as we can get that data from ratio between element length and the data size, but we do need a flag to know whether or not the data is stored directly inside property_entry. Signed-off-by: Dmitry Torokhov [ rjw: Subject & changelog, struct property_entry kerneldoc ] Signed-off-by: Rafael J. Wysocki --- drivers/base/swnode.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index d8d0dc0ca5ac..18a30fb3cc58 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -108,10 +108,7 @@ static const void *property_get_pointer(const struct property_entry *prop) if (!prop->length) return NULL; - if (prop->is_array) - return prop->pointer; - - return &prop->value; + return prop->is_inline ? &prop->value : prop->pointer; } static const void *property_entry_find(const struct property_entry *props, @@ -205,7 +202,7 @@ static void property_entry_free_data(const struct property_entry *p) const char * const *src_str; size_t i, nval; - if (p->is_array) { + if (!p->is_inline) { if (p->type == DEV_PROP_STRING && p->pointer) { src_str = p->pointer; nval = p->length / sizeof(const char *); @@ -250,7 +247,7 @@ static int property_entry_copy_data(struct property_entry *dst, const void *pointer = property_get_pointer(src); const void *new; - if (src->is_array) { + if (!src->is_inline) { if (!src->length) return -ENODATA; @@ -264,15 +261,16 @@ static int property_entry_copy_data(struct property_entry *dst, return -ENOMEM; } - dst->is_array = true; dst->pointer = new; } else if (src->type == DEV_PROP_STRING) { new = kstrdup(src->value.str, GFP_KERNEL); if (!new && src->value.str) return -ENOMEM; + dst->is_inline = true; dst->value.str = new; } else { + dst->is_inline = true; dst->value = src->value; } -- cgit v1.2.3-59-g8ed1b From 996b0830f95d132e50891a5568fef9e2965e4af2 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 7 Nov 2019 20:22:21 -0800 Subject: software node: allow embedding of small arrays into property_entry We should not conflate whether a property data is an array or a single value with where it is stored (embedded into property_entry structure or out-of-line). All single-value properties are in effect 1-element arrays, and we can figure the amount of data stored in a property by examining its length and the data type. And arrays can be as easily stored in property entry instances as single values are, provided that we have enough space (we have up to 8 bytes). We can embed: - up to 8 bytes from U8 arrays - up to 4 words - up to 2 double words - one U64 value - one (on 64 bit architectures) or 2 (on 32 bit) strings. This change also has an effect of switching properties with small amount of data to embed it instead of keeping it separate when copying such properties. Signed-off-by: Dmitry Torokhov Signed-off-by: Rafael J. Wysocki --- drivers/base/swnode.c | 115 +++++++++++++++++++++++------------------------ include/linux/property.h | 14 +++--- 2 files changed, 62 insertions(+), 67 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index 18a30fb3cc58..3d422918a53d 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -198,93 +198,84 @@ static int property_entry_read_string_array(const struct property_entry *props, static void property_entry_free_data(const struct property_entry *p) { - const void *pointer = property_get_pointer(p); const char * const *src_str; size_t i, nval; - if (!p->is_inline) { - if (p->type == DEV_PROP_STRING && p->pointer) { - src_str = p->pointer; - nval = p->length / sizeof(const char *); - for (i = 0; i < nval; i++) - kfree(src_str[i]); - } - kfree(pointer); - } else if (p->type == DEV_PROP_STRING) { - kfree(p->value.str); + if (p->type == DEV_PROP_STRING) { + src_str = property_get_pointer(p); + nval = p->length / sizeof(*src_str); + for (i = 0; i < nval; i++) + kfree(src_str[i]); } + + if (!p->is_inline) + kfree(p->pointer); + kfree(p->name); } -static const char * const * -property_copy_string_array(const struct property_entry *src) +static bool property_copy_string_array(const char **dst_ptr, + const char * const *src_ptr, + size_t nval) { - const char **d; - const char * const *src_str = src->pointer; - size_t nval = src->length / sizeof(*d); int i; - d = kcalloc(nval, sizeof(*d), GFP_KERNEL); - if (!d) - return NULL; - for (i = 0; i < nval; i++) { - d[i] = kstrdup(src_str[i], GFP_KERNEL); - if (!d[i] && src_str[i]) { + dst_ptr[i] = kstrdup(src_ptr[i], GFP_KERNEL); + if (!dst_ptr[i] && src_ptr[i]) { while (--i >= 0) - kfree(d[i]); - kfree(d); - return NULL; + kfree(dst_ptr[i]); + return false; } } - return d; + return true; } static int property_entry_copy_data(struct property_entry *dst, const struct property_entry *src) { const void *pointer = property_get_pointer(src); - const void *new; - - if (!src->is_inline) { - if (!src->length) - return -ENODATA; - - if (src->type == DEV_PROP_STRING) { - new = property_copy_string_array(src); - if (!new) - return -ENOMEM; - } else { - new = kmemdup(pointer, src->length, GFP_KERNEL); - if (!new) - return -ENOMEM; - } - - dst->pointer = new; - } else if (src->type == DEV_PROP_STRING) { - new = kstrdup(src->value.str, GFP_KERNEL); - if (!new && src->value.str) + void *dst_ptr; + size_t nval; + + /* + * Properties with no data should not be marked as stored + * out of line. + */ + if (!src->is_inline && !src->length) + return -ENODATA; + + if (src->length <= sizeof(dst->value)) { + dst_ptr = &dst->value; + dst->is_inline = true; + } else { + dst_ptr = kmalloc(src->length, GFP_KERNEL); + if (!dst_ptr) return -ENOMEM; + dst->pointer = dst_ptr; + } - dst->is_inline = true; - dst->value.str = new; + if (src->type == DEV_PROP_STRING) { + nval = src->length / sizeof(const char *); + if (!property_copy_string_array(dst_ptr, pointer, nval)) { + if (!dst->is_inline) + kfree(dst->pointer); + return -ENOMEM; + } } else { - dst->is_inline = true; - dst->value = src->value; + memcpy(dst_ptr, pointer, src->length); } dst->length = src->length; dst->type = src->type; dst->name = kstrdup(src->name, GFP_KERNEL); - if (!dst->name) - goto out_free_data; + if (!dst->name) { + property_entry_free_data(dst); + return -ENOMEM; + } return 0; - -out_free_data: - property_entry_free_data(dst); - return -ENOMEM; } /** @@ -484,6 +475,8 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode, const struct software_node_reference *ref; const struct property_entry *prop; struct fwnode_handle *refnode; + u32 nargs_prop_val; + int error; int i; if (!swnode || !swnode->node->references) @@ -501,11 +494,13 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode, return -ENOENT; if (nargs_prop) { - prop = property_entry_get(swnode->node->properties, nargs_prop); - if (!prop) - return -EINVAL; + error = property_entry_read_int_array(swnode->node->properties, + nargs_prop, sizeof(u32), + &nargs_prop_val, 1); + if (error) + return error; - nargs = prop->value.u32_data; + nargs = nargs_prop_val; } if (nargs > NR_FWNODE_REFERENCE_ARGS) diff --git a/include/linux/property.h b/include/linux/property.h index 6c1ca870a9a9..fc819c6ebf33 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -240,11 +240,11 @@ struct property_entry { union { const void *pointer; union { - u8 u8_data; - u16 u16_data; - u32 u32_data; - u64 u64_data; - const char *str; + u8 u8_data[sizeof(u64) / sizeof(u8)]; + u16 u16_data[sizeof(u64) / sizeof(u16)]; + u32 u32_data[sizeof(u64) / sizeof(u32)]; + u64 u64_data[sizeof(u64) / sizeof(u64)]; + const char *str[sizeof(u64) / sizeof(char *)]; } value; }; }; @@ -256,7 +256,7 @@ struct property_entry { */ #define __PROPERTY_ENTRY_ELEMENT_SIZE(_elem_) \ - sizeof(((struct property_entry *)NULL)->value._elem_) + sizeof(((struct property_entry *)NULL)->value._elem_[0]) #define __PROPERTY_ENTRY_ARRAY_LEN(_name_, _elem_, _Type_, _val_, _len_)\ (struct property_entry) { \ @@ -294,7 +294,7 @@ struct property_entry { .length = __PROPERTY_ENTRY_ELEMENT_SIZE(_elem_), \ .is_inline = true, \ .type = DEV_PROP_##_Type_, \ - { .value = { ._elem_ = _val_ } }, \ + { .value = { ._elem_[0] = _val_ } }, \ } #define PROPERTY_ENTRY_U8(_name_, _val_) \ -- cgit v1.2.3-59-g8ed1b From e64b674bc9d76edb4cf1b8c98446b1d29a16b9df Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 7 Nov 2019 20:22:22 -0800 Subject: software node: implement reference properties It is possible to store references to software nodes in the same fashion as other static properties, so that users do not need to define separate structures: static const struct software_node gpio_bank_b_node = { .name = "B", }; static const struct property_entry simone_key_enter_props[] = { PROPERTY_ENTRY_U32("linux,code", KEY_ENTER), PROPERTY_ENTRY_STRING("label", "enter"), PROPERTY_ENTRY_REF("gpios", &gpio_bank_b_node, 123, GPIO_ACTIVE_LOW), { } }; Signed-off-by: Dmitry Torokhov Signed-off-by: Rafael J. Wysocki --- drivers/base/swnode.c | 49 ++++++++++++++++++++++++++++++++++------- include/linux/property.h | 57 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 82 insertions(+), 24 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index 3d422918a53d..604d7327bba7 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -246,6 +246,13 @@ static int property_entry_copy_data(struct property_entry *dst, if (!src->is_inline && !src->length) return -ENODATA; + /* + * Reference properties are never stored inline as + * they are too big. + */ + if (src->type == DEV_PROP_REF && src->is_inline) + return -EINVAL; + if (src->length <= sizeof(dst->value)) { dst_ptr = &dst->value; dst->is_inline = true; @@ -473,23 +480,49 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode, { struct swnode *swnode = to_swnode(fwnode); const struct software_node_reference *ref; + const struct software_node_ref_args *ref_array; + const struct software_node_ref_args *ref_args; const struct property_entry *prop; struct fwnode_handle *refnode; u32 nargs_prop_val; int error; int i; - if (!swnode || !swnode->node->references) + if (!swnode) return -ENOENT; - for (ref = swnode->node->references; ref->name; ref++) - if (!strcmp(ref->name, propname)) - break; + prop = property_entry_get(swnode->node->properties, propname); + if (prop) { + if (prop->type != DEV_PROP_REF) + return -EINVAL; - if (!ref->name || index > (ref->nrefs - 1)) - return -ENOENT; + /* + * We expect that references are never stored inline, even + * single ones, as they are too big. + */ + if (prop->is_inline) + return -EINVAL; + + if (index * sizeof(*ref_args) >= prop->length) + return -ENOENT; + + ref_array = prop->pointer; + ref_args = &ref_array[index]; + } else { + if (!swnode->node->references) + return -ENOENT; + + for (ref = swnode->node->references; ref->name; ref++) + if (!strcmp(ref->name, propname)) + break; + + if (!ref->name || index > (ref->nrefs - 1)) + return -ENOENT; + + ref_args = &ref->refs[index]; + } - refnode = software_node_fwnode(ref->refs[index].node); + refnode = software_node_fwnode(ref_args->node); if (!refnode) return -ENOENT; @@ -510,7 +543,7 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode, args->nargs = nargs; for (i = 0; i < nargs; i++) - args->args[i] = ref->refs[index].args[i]; + args->args[i] = ref_args->args[i]; return 0; } diff --git a/include/linux/property.h b/include/linux/property.h index fc819c6ebf33..3df7089f0dbd 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -22,6 +22,7 @@ enum dev_prop_type { DEV_PROP_U32, DEV_PROP_U64, DEV_PROP_STRING, + DEV_PROP_REF, }; enum dev_dma_attr { @@ -223,6 +224,20 @@ static inline int fwnode_property_count_u64(const struct fwnode_handle *fwnode, return fwnode_property_read_u64_array(fwnode, propname, NULL, 0); } +struct software_node; + +/** + * struct software_node_ref_args - Reference property with additional arguments + * @node: Reference to a software node + * @nargs: Number of elements in @args array + * @args: Integer arguments + */ +struct software_node_ref_args { + const struct software_node *node; + unsigned int nargs; + u64 args[NR_FWNODE_REFERENCE_ARGS]; +}; + /** * struct property_entry - "Built-in" device property representation. * @name: Name of the property. @@ -258,14 +273,20 @@ struct property_entry { #define __PROPERTY_ENTRY_ELEMENT_SIZE(_elem_) \ sizeof(((struct property_entry *)NULL)->value._elem_[0]) -#define __PROPERTY_ENTRY_ARRAY_LEN(_name_, _elem_, _Type_, _val_, _len_)\ +#define __PROPERTY_ENTRY_ARRAY_ELSIZE_LEN(_name_, _elsize_, _Type_, \ + _val_, _len_) \ (struct property_entry) { \ .name = _name_, \ - .length = (_len_) * __PROPERTY_ENTRY_ELEMENT_SIZE(_elem_), \ + .length = (_len_) * (_elsize_), \ .type = DEV_PROP_##_Type_, \ { .pointer = _val_ }, \ } +#define __PROPERTY_ENTRY_ARRAY_LEN(_name_, _elem_, _Type_, _val_, _len_)\ + __PROPERTY_ENTRY_ARRAY_ELSIZE_LEN(_name_, \ + __PROPERTY_ENTRY_ELEMENT_SIZE(_elem_), \ + _Type_, _val_, _len_) + #define PROPERTY_ENTRY_U8_ARRAY_LEN(_name_, _val_, _len_) \ __PROPERTY_ENTRY_ARRAY_LEN(_name_, u8_data, U8, _val_, _len_) #define PROPERTY_ENTRY_U16_ARRAY_LEN(_name_, _val_, _len_) \ @@ -276,6 +297,10 @@ struct property_entry { __PROPERTY_ENTRY_ARRAY_LEN(_name_, u64_data, U64, _val_, _len_) #define PROPERTY_ENTRY_STRING_ARRAY_LEN(_name_, _val_, _len_) \ __PROPERTY_ENTRY_ARRAY_LEN(_name_, str, STRING, _val_, _len_) +#define PROPERTY_ENTRY_REF_ARRAY_LEN(_name_, _val_, _len_) \ + __PROPERTY_ENTRY_ARRAY_ELSIZE_LEN(_name_, \ + sizeof(struct software_node_ref_args), \ + REF, _val_, _len_) #define PROPERTY_ENTRY_U8_ARRAY(_name_, _val_) \ PROPERTY_ENTRY_U8_ARRAY_LEN(_name_, _val_, ARRAY_SIZE(_val_)) @@ -287,6 +312,8 @@ struct property_entry { PROPERTY_ENTRY_U64_ARRAY_LEN(_name_, _val_, ARRAY_SIZE(_val_)) #define PROPERTY_ENTRY_STRING_ARRAY(_name_, _val_) \ PROPERTY_ENTRY_STRING_ARRAY_LEN(_name_, _val_, ARRAY_SIZE(_val_)) +#define PROPERTY_ENTRY_REF_ARRAY(_name_, _val_) \ + PROPERTY_ENTRY_REF_ARRAY_LEN(_name_, _val_, ARRAY_SIZE(_val_)) #define __PROPERTY_ENTRY_ELEMENT(_name_, _elem_, _Type_, _val_) \ (struct property_entry) { \ @@ -314,6 +341,18 @@ struct property_entry { .is_inline = true, \ } +#define PROPERTY_ENTRY_REF(_name_, _ref_, ...) \ +(struct property_entry) { \ + .name = _name_, \ + .length = sizeof(struct software_node_ref_args), \ + .type = DEV_PROP_REF, \ + { .pointer = &(const struct software_node_ref_args) { \ + .node = _ref_, \ + .nargs = ARRAY_SIZE(((u64[]){ 0, ##__VA_ARGS__ })) - 1, \ + .args = { __VA_ARGS__ }, \ + } }, \ +} + struct property_entry * property_entries_dup(const struct property_entry *properties); @@ -377,20 +416,6 @@ int fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode, /* -------------------------------------------------------------------------- */ /* Software fwnode support - when HW description is incomplete or missing */ -struct software_node; - -/** - * struct software_node_ref_args - Reference with additional arguments - * @node: Reference to a software node - * @nargs: Number of elements in @args array - * @args: Integer arguments - */ -struct software_node_ref_args { - const struct software_node *node; - unsigned int nargs; - u64 args[NR_FWNODE_REFERENCE_ARGS]; -}; - /** * struct software_node_reference - Named software node reference property * @name: Name of the property -- cgit v1.2.3-59-g8ed1b From e933bedd45099dce1165104138bb703a6e31df82 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 7 Nov 2019 20:22:24 -0800 Subject: software node: remove separate handling of references Now that all users of references have moved to reference properties, we can remove separate handling of references. Signed-off-by: Dmitry Torokhov Signed-off-by: Rafael J. Wysocki --- drivers/base/swnode.c | 48 ++++++++++++++++++------------------------------ include/linux/property.h | 14 -------------- 2 files changed, 18 insertions(+), 44 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index 604d7327bba7..0b081dee1e95 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -479,9 +479,8 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode, struct fwnode_reference_args *args) { struct swnode *swnode = to_swnode(fwnode); - const struct software_node_reference *ref; const struct software_node_ref_args *ref_array; - const struct software_node_ref_args *ref_args; + const struct software_node_ref_args *ref; const struct property_entry *prop; struct fwnode_handle *refnode; u32 nargs_prop_val; @@ -492,37 +491,26 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode, return -ENOENT; prop = property_entry_get(swnode->node->properties, propname); - if (prop) { - if (prop->type != DEV_PROP_REF) - return -EINVAL; - - /* - * We expect that references are never stored inline, even - * single ones, as they are too big. - */ - if (prop->is_inline) - return -EINVAL; - - if (index * sizeof(*ref_args) >= prop->length) - return -ENOENT; - - ref_array = prop->pointer; - ref_args = &ref_array[index]; - } else { - if (!swnode->node->references) - return -ENOENT; + if (!prop) + return -ENOENT; + + if (prop->type != DEV_PROP_REF) + return -EINVAL; - for (ref = swnode->node->references; ref->name; ref++) - if (!strcmp(ref->name, propname)) - break; + /* + * We expect that references are never stored inline, even + * single ones, as they are too big. + */ + if (prop->is_inline) + return -EINVAL; - if (!ref->name || index > (ref->nrefs - 1)) - return -ENOENT; + if (index * sizeof(*ref) >= prop->length) + return -ENOENT; - ref_args = &ref->refs[index]; - } + ref_array = prop->pointer; + ref = &ref_array[index]; - refnode = software_node_fwnode(ref_args->node); + refnode = software_node_fwnode(ref->node); if (!refnode) return -ENOENT; @@ -543,7 +531,7 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode, args->nargs = nargs; for (i = 0; i < nargs; i++) - args->args[i] = ref_args->args[i]; + args->args[i] = ref->args[i]; return 0; } diff --git a/include/linux/property.h b/include/linux/property.h index 3df7089f0dbd..d86de017c689 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -416,30 +416,16 @@ int fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode, /* -------------------------------------------------------------------------- */ /* Software fwnode support - when HW description is incomplete or missing */ -/** - * struct software_node_reference - Named software node reference property - * @name: Name of the property - * @nrefs: Number of elements in @refs array - * @refs: Array of references with optional arguments - */ -struct software_node_reference { - const char *name; - unsigned int nrefs; - const struct software_node_ref_args *refs; -}; - /** * struct software_node - Software node description * @name: Name of the software node * @parent: Parent of the software node * @properties: Array of device properties - * @references: Array of software node reference properties */ struct software_node { const char *name; const struct software_node *parent; const struct property_entry *properties; - const struct software_node_reference *references; }; bool is_software_node(const struct fwnode_handle *fwnode); -- cgit v1.2.3-59-g8ed1b From c032ace71c29d513bf9df64ace1885fe5ff24981 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 4 Dec 2019 10:53:15 -0800 Subject: software node: add basic tests for property entries This adds tests for creating software nodes with properties supplied by PROPERTY_ENTRY_XXX() macros and fetching and validating data from said nodes/properties. We are using KUnit framework for the tests. Signed-off-by: Dmitry Torokhov Signed-off-by: Rafael J. Wysocki --- drivers/base/test/Makefile | 2 + drivers/base/test/property-entry-test.c | 474 ++++++++++++++++++++++++++++++++ 2 files changed, 476 insertions(+) create mode 100644 drivers/base/test/property-entry-test.c (limited to 'drivers/base') diff --git a/drivers/base/test/Makefile b/drivers/base/test/Makefile index 0f1f7277a013..22143102e5d2 100644 --- a/drivers/base/test/Makefile +++ b/drivers/base/test/Makefile @@ -1,2 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TEST_ASYNC_DRIVER_PROBE) += test_async_driver_probe.o + +obj-$(CONFIG_KUNIT) += property-entry-test.o diff --git a/drivers/base/test/property-entry-test.c b/drivers/base/test/property-entry-test.c new file mode 100644 index 000000000000..da812834b631 --- /dev/null +++ b/drivers/base/test/property-entry-test.c @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: GPL-2.0 +// Unit tests for property entries API +// +// Copyright 2019 Google LLC. + +#include +#include +#include + +static void pe_test_uints(struct kunit *test) +{ + static const struct property_entry entries[] = { + PROPERTY_ENTRY_U8("prop-u8", 8), + PROPERTY_ENTRY_U16("prop-u16", 16), + PROPERTY_ENTRY_U32("prop-u32", 32), + PROPERTY_ENTRY_U64("prop-u64", 64), + { } + }; + + struct fwnode_handle *node; + u8 val_u8, array_u8[2]; + u16 val_u16, array_u16[2]; + u32 val_u32, array_u32[2]; + u64 val_u64, array_u64[2]; + int error; + + node = fwnode_create_software_node(entries, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); + + error = fwnode_property_read_u8(node, "prop-u8", &val_u8); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)val_u8, 8); + + error = fwnode_property_read_u8_array(node, "prop-u8", array_u8, 1); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)array_u8[0], 8); + + error = fwnode_property_read_u8_array(node, "prop-u8", array_u8, 2); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u8(node, "no-prop-u8", &val_u8); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u8_array(node, "no-prop-u8", array_u8, 1); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u16(node, "prop-u16", &val_u16); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)val_u16, 16); + + error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 1); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)array_u16[0], 16); + + error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 2); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u16(node, "no-prop-u16", &val_u16); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u16_array(node, "no-prop-u16", array_u16, 1); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u32(node, "prop-u32", &val_u32); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)val_u32, 32); + + error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 1); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)array_u32[0], 32); + + error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 2); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u32(node, "no-prop-u32", &val_u32); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u32_array(node, "no-prop-u32", array_u32, 1); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u64(node, "prop-u64", &val_u64); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)val_u64, 64); + + error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 1); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)array_u64[0], 64); + + error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 2); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u64(node, "no-prop-u64", &val_u64); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u64_array(node, "no-prop-u64", array_u64, 1); + KUNIT_EXPECT_NE(test, error, 0); + + fwnode_remove_software_node(node); +} + +static void pe_test_uint_arrays(struct kunit *test) +{ + static const u8 a_u8[16] = { 8, 9 }; + static const u16 a_u16[16] = { 16, 17 }; + static const u32 a_u32[16] = { 32, 33 }; + static const u64 a_u64[16] = { 64, 65 }; + static const struct property_entry entries[] = { + PROPERTY_ENTRY_U8_ARRAY("prop-u8", a_u8), + PROPERTY_ENTRY_U16_ARRAY("prop-u16", a_u16), + PROPERTY_ENTRY_U32_ARRAY("prop-u32", a_u32), + PROPERTY_ENTRY_U64_ARRAY("prop-u64", a_u64), + { } + }; + + struct fwnode_handle *node; + u8 val_u8, array_u8[32]; + u16 val_u16, array_u16[32]; + u32 val_u32, array_u32[32]; + u64 val_u64, array_u64[32]; + int error; + + node = fwnode_create_software_node(entries, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); + + error = fwnode_property_read_u8(node, "prop-u8", &val_u8); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)val_u8, 8); + + error = fwnode_property_read_u8_array(node, "prop-u8", array_u8, 1); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)array_u8[0], 8); + + error = fwnode_property_read_u8_array(node, "prop-u8", array_u8, 2); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)array_u8[0], 8); + KUNIT_EXPECT_EQ(test, (int)array_u8[1], 9); + + error = fwnode_property_read_u8_array(node, "prop-u8", array_u8, 17); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u8(node, "no-prop-u8", &val_u8); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u8_array(node, "no-prop-u8", array_u8, 1); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u16(node, "prop-u16", &val_u16); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)val_u16, 16); + + error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 1); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)array_u16[0], 16); + + error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 2); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)array_u16[0], 16); + KUNIT_EXPECT_EQ(test, (int)array_u16[1], 17); + + error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 17); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u16(node, "no-prop-u16", &val_u16); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u16_array(node, "no-prop-u16", array_u16, 1); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u32(node, "prop-u32", &val_u32); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)val_u32, 32); + + error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 1); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)array_u32[0], 32); + + error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 2); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)array_u32[0], 32); + KUNIT_EXPECT_EQ(test, (int)array_u32[1], 33); + + error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 17); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u32(node, "no-prop-u32", &val_u32); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u32_array(node, "no-prop-u32", array_u32, 1); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u64(node, "prop-u64", &val_u64); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)val_u64, 64); + + error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 1); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)array_u64[0], 64); + + error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 2); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_EQ(test, (int)array_u64[0], 64); + KUNIT_EXPECT_EQ(test, (int)array_u64[1], 65); + + error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 17); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u64(node, "no-prop-u64", &val_u64); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_u64_array(node, "no-prop-u64", array_u64, 1); + KUNIT_EXPECT_NE(test, error, 0); + + fwnode_remove_software_node(node); +} + +static void pe_test_strings(struct kunit *test) +{ + static const char *strings[] = { + "string-a", + "string-b", + }; + + static const struct property_entry entries[] = { + PROPERTY_ENTRY_STRING("str", "single"), + PROPERTY_ENTRY_STRING("empty", ""), + PROPERTY_ENTRY_STRING_ARRAY("strs", strings), + { } + }; + + struct fwnode_handle *node; + const char *str; + const char *strs[10]; + int error; + + node = fwnode_create_software_node(entries, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); + + error = fwnode_property_read_string(node, "str", &str); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_STREQ(test, str, "single"); + + error = fwnode_property_read_string_array(node, "str", strs, 1); + KUNIT_EXPECT_EQ(test, error, 1); + KUNIT_EXPECT_STREQ(test, strs[0], "single"); + + /* asking for more data returns what we have */ + error = fwnode_property_read_string_array(node, "str", strs, 2); + KUNIT_EXPECT_EQ(test, error, 1); + KUNIT_EXPECT_STREQ(test, strs[0], "single"); + + error = fwnode_property_read_string(node, "no-str", &str); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_read_string_array(node, "no-str", strs, 1); + KUNIT_EXPECT_LT(test, error, 0); + + error = fwnode_property_read_string(node, "empty", &str); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_STREQ(test, str, ""); + + error = fwnode_property_read_string_array(node, "strs", strs, 3); + KUNIT_EXPECT_EQ(test, error, 2); + KUNIT_EXPECT_STREQ(test, strs[0], "string-a"); + KUNIT_EXPECT_STREQ(test, strs[1], "string-b"); + + error = fwnode_property_read_string_array(node, "strs", strs, 1); + KUNIT_EXPECT_EQ(test, error, 1); + KUNIT_EXPECT_STREQ(test, strs[0], "string-a"); + + /* NULL argument -> returns size */ + error = fwnode_property_read_string_array(node, "strs", NULL, 0); + KUNIT_EXPECT_EQ(test, error, 2); + + /* accessing array as single value */ + error = fwnode_property_read_string(node, "strs", &str); + KUNIT_EXPECT_EQ(test, error, 0); + KUNIT_EXPECT_STREQ(test, str, "string-a"); + + fwnode_remove_software_node(node); +} + +static void pe_test_bool(struct kunit *test) +{ + static const struct property_entry entries[] = { + PROPERTY_ENTRY_BOOL("prop"), + { } + }; + + struct fwnode_handle *node; + + node = fwnode_create_software_node(entries, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); + + KUNIT_EXPECT_TRUE(test, fwnode_property_read_bool(node, "prop")); + KUNIT_EXPECT_FALSE(test, fwnode_property_read_bool(node, "not-prop")); + + fwnode_remove_software_node(node); +} + +/* Verifies that small U8 array is stored inline when property is copied */ +static void pe_test_move_inline_u8(struct kunit *test) +{ + static const u8 u8_array_small[8] = { 1, 2, 3, 4 }; + static const u8 u8_array_big[128] = { 5, 6, 7, 8 }; + static const struct property_entry entries[] = { + PROPERTY_ENTRY_U8_ARRAY("small", u8_array_small), + PROPERTY_ENTRY_U8_ARRAY("big", u8_array_big), + { } + }; + + struct property_entry *copy; + const u8 *data_ptr; + + copy = property_entries_dup(entries); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, copy); + + KUNIT_EXPECT_TRUE(test, copy[0].is_inline); + data_ptr = (u8 *)©[0].value; + KUNIT_EXPECT_EQ(test, (int)data_ptr[0], 1); + KUNIT_EXPECT_EQ(test, (int)data_ptr[1], 2); + + KUNIT_EXPECT_FALSE(test, copy[1].is_inline); + data_ptr = copy[1].pointer; + KUNIT_EXPECT_EQ(test, (int)data_ptr[0], 5); + KUNIT_EXPECT_EQ(test, (int)data_ptr[1], 6); + + property_entries_free(copy); +} + +/* Verifies that single string array is stored inline when property is copied */ +static void pe_test_move_inline_str(struct kunit *test) +{ + static char *str_array_small[] = { "a" }; + static char *str_array_big[] = { "b", "c", "d", "e" }; + static char *str_array_small_empty[] = { "" }; + static struct property_entry entries[] = { + PROPERTY_ENTRY_STRING_ARRAY("small", str_array_small), + PROPERTY_ENTRY_STRING_ARRAY("big", str_array_big), + PROPERTY_ENTRY_STRING_ARRAY("small-empty", str_array_small_empty), + { } + }; + + struct property_entry *copy; + const char * const *data_ptr; + + copy = property_entries_dup(entries); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, copy); + + KUNIT_EXPECT_TRUE(test, copy[0].is_inline); + KUNIT_EXPECT_STREQ(test, copy[0].value.str[0], "a"); + + KUNIT_EXPECT_FALSE(test, copy[1].is_inline); + data_ptr = copy[1].pointer; + KUNIT_EXPECT_STREQ(test, data_ptr[0], "b"); + KUNIT_EXPECT_STREQ(test, data_ptr[1], "c"); + + KUNIT_EXPECT_TRUE(test, copy[2].is_inline); + KUNIT_EXPECT_STREQ(test, copy[2].value.str[0], ""); + + property_entries_free(copy); +} + +/* Handling of reference properties */ +static void pe_test_reference(struct kunit *test) +{ + static const struct software_node nodes[] = { + { .name = "1", }, + { .name = "2", }, + }; + + static const struct software_node_ref_args refs[] = { + { + .node = &nodes[0], + .nargs = 0, + }, + { + .node = &nodes[1], + .nargs = 2, + .args = { 3, 4 }, + }, + }; + + const struct property_entry entries[] = { + PROPERTY_ENTRY_REF("ref-1", &nodes[0]), + PROPERTY_ENTRY_REF("ref-2", &nodes[1], 1, 2), + PROPERTY_ENTRY_REF_ARRAY("ref-3", refs), + { } + }; + + struct fwnode_handle *node; + struct fwnode_reference_args ref; + int error; + + error = software_node_register_nodes(nodes); + KUNIT_ASSERT_EQ(test, error, 0); + + node = fwnode_create_software_node(entries, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); + + error = fwnode_property_get_reference_args(node, "ref-1", NULL, + 0, 0, &ref); + KUNIT_ASSERT_EQ(test, error, 0); + KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &nodes[0]); + KUNIT_EXPECT_EQ(test, ref.nargs, 0U); + + /* wrong index */ + error = fwnode_property_get_reference_args(node, "ref-1", NULL, + 0, 1, &ref); + KUNIT_EXPECT_NE(test, error, 0); + + error = fwnode_property_get_reference_args(node, "ref-2", NULL, + 1, 0, &ref); + KUNIT_ASSERT_EQ(test, error, 0); + KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &nodes[1]); + KUNIT_EXPECT_EQ(test, ref.nargs, 1U); + KUNIT_EXPECT_EQ(test, ref.args[0], 1LLU); + + /* asking for more args, padded with zero data */ + error = fwnode_property_get_reference_args(node, "ref-2", NULL, + 3, 0, &ref); + KUNIT_ASSERT_EQ(test, error, 0); + KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &nodes[1]); + KUNIT_EXPECT_EQ(test, ref.nargs, 3U); + KUNIT_EXPECT_EQ(test, ref.args[0], 1LLU); + KUNIT_EXPECT_EQ(test, ref.args[1], 2LLU); + KUNIT_EXPECT_EQ(test, ref.args[2], 0LLU); + + /* wrong index */ + error = fwnode_property_get_reference_args(node, "ref-2", NULL, + 2, 1, &ref); + KUNIT_EXPECT_NE(test, error, 0); + + /* array of references */ + error = fwnode_property_get_reference_args(node, "ref-3", NULL, + 0, 0, &ref); + KUNIT_ASSERT_EQ(test, error, 0); + KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &nodes[0]); + KUNIT_EXPECT_EQ(test, ref.nargs, 0U); + + /* second reference in the array */ + error = fwnode_property_get_reference_args(node, "ref-3", NULL, + 2, 1, &ref); + KUNIT_ASSERT_EQ(test, error, 0); + KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &nodes[1]); + KUNIT_EXPECT_EQ(test, ref.nargs, 2U); + KUNIT_EXPECT_EQ(test, ref.args[0], 3LLU); + KUNIT_EXPECT_EQ(test, ref.args[1], 4LLU); + + /* wrong index */ + error = fwnode_property_get_reference_args(node, "ref-1", NULL, + 0, 2, &ref); + KUNIT_EXPECT_NE(test, error, 0); + + fwnode_remove_software_node(node); + software_node_unregister_nodes(nodes); +} + +static struct kunit_case property_entry_test_cases[] = { + KUNIT_CASE(pe_test_uints), + KUNIT_CASE(pe_test_uint_arrays), + KUNIT_CASE(pe_test_strings), + KUNIT_CASE(pe_test_bool), + KUNIT_CASE(pe_test_move_inline_u8), + KUNIT_CASE(pe_test_move_inline_str), + KUNIT_CASE(pe_test_reference), + { } +}; + +static struct kunit_suite property_entry_test_suite = { + .name = "property-entry", + .test_cases = property_entry_test_cases, +}; + +kunit_test_suite(property_entry_test_suite); -- cgit v1.2.3-59-g8ed1b From eabd5e7d8bf5994ad2e3aff22b84596b5b5485e8 Mon Sep 17 00:00:00 2001 From: Qian Cai Date: Mon, 6 Jan 2020 11:37:35 -0500 Subject: drivers/base/test: fix global-out-of-bounds error Commit c032ace71c29 ("software node: add basic tests for property entries") introduced a global-out-of-bounds error because it forgot to add a terminator of "nodes "for software_node_register_nodes() to process. # Subtest: property-entry 1..7 ok 1 - pe_test_uints ok 2 - pe_test_uint_arrays ok 3 - pe_test_strings ok 4 - pe_test_bool ok 5 - pe_test_move_inline_u8 ok 6 - pe_test_move_inline_str ================================================================== BUG: KASAN: global-out-of-bounds in software_node_register_nodes+0x41/0x80 Read of size 8 at addr ffffffff989ef250 by task kunit_try_catch/316 CPU: 17 PID: 316 Comm: kunit_try_catch Not tainted 5.5.0-rc4-next-20200106+ #1 Hardware name: HPE ProLiant DL385 Gen10/ProLiant DL385 Gen10, BIOS A40 03/09/2018 Call Trace: dump_stack+0xa0/0xea print_address_description.constprop.5.cold.7+0x64/0x384 __kasan_report.cold.8+0x7a/0xc0 kasan_report+0x12/0x20 __asan_load8+0x71/0xa0 software_node_register_nodes+0x41/0x80 pe_test_reference+0x1eb/0x1200 kunit_try_run_case+0x6b/0xd1 kunit_generic_run_threadfn_adapter+0x29/0x50 kthread+0x1e6/0x210 ret_from_fork+0x27/0x50 The buggy address belongs to the variable: nodes.21544+0x30/0x920 Memory state around the buggy address: ffffffff989ef100: fa fa fa fa 00 04 fa fa fa fa fa fa 00 00 00 00 ffffffff989ef180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >ffffffff989ef200: fa fa fa fa 00 00 00 00 00 00 fa fa fa fa fa fa ^ ffffffff989ef280: 00 06 fa fa fa fa fa fa 00 00 04 fa fa fa fa fa ffffffff989ef300: 00 00 fa fa fa fa fa fa 00 05 fa fa fa fa fa fa ================================================================== Disabling lock debugging due to kernel taint ok 7 - pe_test_reference ok 8 - property-entry Fixes: c032ace71c29 ("software node: add basic tests for property entries") Signed-off-by: Qian Cai Reviewed-by: Dmitry Torokhov Signed-off-by: Rafael J. Wysocki --- drivers/base/test/property-entry-test.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/base') diff --git a/drivers/base/test/property-entry-test.c b/drivers/base/test/property-entry-test.c index da812834b631..abe03315180f 100644 --- a/drivers/base/test/property-entry-test.c +++ b/drivers/base/test/property-entry-test.c @@ -366,6 +366,7 @@ static void pe_test_reference(struct kunit *test) static const struct software_node nodes[] = { { .name = "1", }, { .name = "2", }, + { } }; static const struct software_node_ref_args refs[] = { -- cgit v1.2.3-59-g8ed1b From aa811e3cececac2f65f7fa7e17ab46c73d778b2b Mon Sep 17 00:00:00 2001 From: Alan Maguire Date: Tue, 14 Jan 2020 16:09:43 +0000 Subject: software node: introduce CONFIG_KUNIT_DRIVER_PE_TEST Currently the property entry kunit tests are built if CONFIG_KUNIT=y. This will cause warnings when merged with the kunit tree that now supports tristate CONFIG_KUNIT. While the tests appear to compile as a module, we get a warning about missing module license. It's better to have a per-test suite CONFIG variable so that we can do selective building of kunit-based suites, and can also avoid merge issues like this. Fixes: c032ace71c29 ("software node: add basic tests for property entries") Reported-by: Stephen Rothwell Reported-by: Randy Dunlap Signed-off-by: Alan Maguire Signed-off-by: Rafael J. Wysocki --- drivers/base/test/Kconfig | 3 +++ drivers/base/test/Makefile | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/test/Kconfig b/drivers/base/test/Kconfig index 86e85daa80bf..305c7751184a 100644 --- a/drivers/base/test/Kconfig +++ b/drivers/base/test/Kconfig @@ -8,3 +8,6 @@ config TEST_ASYNC_DRIVER_PROBE The module name will be test_async_driver_probe.ko If unsure say N. +config KUNIT_DRIVER_PE_TEST + bool "KUnit Tests for property entry API" + depends on KUNIT=y diff --git a/drivers/base/test/Makefile b/drivers/base/test/Makefile index 22143102e5d2..3ca56367c84b 100644 --- a/drivers/base/test/Makefile +++ b/drivers/base/test/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TEST_ASYNC_DRIVER_PROBE) += test_async_driver_probe.o -obj-$(CONFIG_KUNIT) += property-entry-test.o +obj-$(CONFIG_KUNIT_DRIVER_PE_TEST) += property-entry-test.o -- cgit v1.2.3-59-g8ed1b