/* * Expected message may have a message level other than KERN_INFO. * Print the expected message only if the current loglevel will allow * the actual message to print. * * Do not use EXPECT_BEGIN(), EXPECT_END(), EXPECT_NOT_BEGIN(), or * EXPECT_NOT_END() to report messages expected to be reported or not * reported by pr_debug().
*/ #define EXPECT_BEGIN(level, fmt, ...) \
printk(level pr_fmt("EXPECT \\ : ") fmt, ##__VA_ARGS__)
np = of_find_node_by_path("/testcase-data");
name = kasprintf(GFP_KERNEL, "%pOF", np);
unittest(np && name && !strcmp("/testcase-data", name), "find /testcase-data failed\n");
of_node_put(np);
kfree(name);
/* Test if trailing '/' works */
np = of_find_node_by_path("/testcase-data/");
unittest(!np, "trailing '/' on /testcase-data/ should fail\n");
np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a");
name = kasprintf(GFP_KERNEL, "%pOF", np);
unittest(np && name && !strcmp("/testcase-data/phandle-tests/consumer-a", name), "find /testcase-data/phandle-tests/consumer-a failed\n");
of_node_put(np);
kfree(name);
np = of_find_node_by_path("testcase-alias");
name = kasprintf(GFP_KERNEL, "%pOF", np);
unittest(np && name && !strcmp("/testcase-data", name), "find testcase-alias failed\n");
of_node_put(np);
kfree(name);
/* Test if trailing '/' works on aliases */
np = of_find_node_by_path("testcase-alias/");
unittest(!np, "trailing '/' on testcase-alias/ should fail\n");
np = of_find_node_by_path("testcase-alias/phandle-tests/consumer-a");
name = kasprintf(GFP_KERNEL, "%pOF", np);
unittest(np && name && !strcmp("/testcase-data/phandle-tests/consumer-a", name), "find testcase-alias/phandle-tests/consumer-a failed\n");
of_node_put(np);
kfree(name);
np = of_find_node_by_path("missing-alias");
unittest(!np, "non-existent alias returned node %pOF\n", np);
of_node_put(np);
np = of_find_node_by_path("testcase-alias/missing-path");
unittest(!np, "non-existent alias with relative path returned node %pOF\n", np);
of_node_put(np);
/* Array of 4 properties for the purpose of testing */
prop = kcalloc(4, sizeof(*prop), GFP_KERNEL); if (!prop) {
unittest(0, "kzalloc() failed\n"); return;
}
/* Add a new property - should pass*/
prop->name = "new-property";
prop->value = "new-property-data";
prop->length = strlen(prop->value) + 1;
unittest(of_add_property(np, prop) == 0, "Adding a new property failed\n");
/* Try to add an existing property - should fail */
prop++;
prop->name = "new-property";
prop->value = "new-property-data-should-fail";
prop->length = strlen(prop->value) + 1;
unittest(of_add_property(np, prop) != 0, "Adding an existing property should have failed\n");
/* Try to modify an existing property - should pass */
prop->value = "modify-property-data-should-pass";
prop->length = strlen(prop->value) + 1;
unittest(of_update_property(np, prop) == 0, "Updating an existing property should have passed\n");
/* Try to modify non-existent property - should pass*/
prop++;
prop->name = "modify-property";
prop->value = "modify-missing-property-data-should-pass";
prop->length = strlen(prop->value) + 1;
unittest(of_update_property(np, prop) == 0, "Updating a missing property should have passed\n");
/* Remove property - should pass */
unittest(of_remove_property(np, prop) == 0, "Removing a property should have passed\n");
/* Adding very large property - should pass */
prop++;
prop->name = "large-property-PAGE_SIZEx8";
prop->length = PAGE_SIZE * 8;
prop->value = kzalloc(prop->length, GFP_KERNEL);
unittest(prop->value != NULL, "Unable to allocate large buffer\n"); if (prop->value)
unittest(of_add_property(np, prop) == 0, "Adding a large property should have passed\n");
}
/* Baseline; check conversion with a large size limit */
memset(buf, 0xff, buf_size);
size = snprintf(buf, buf_size - 2, fmt, np);
/* use strcmp() instead of strncmp() here to be absolutely sure strings match */
unittest((strcmp(buf, expected) == 0) && (buf[size+1] == 0xff), "sprintf failed; fmt='%s' expected='%s' rslt='%s'\n",
fmt, expected, buf);
/* Make sure length limits work */
size++; for (i = 0; i < 2; i++, size--) { /* Clear the buffer, and make sure it works correctly still */
memset(buf, 0xff, buf_size);
snprintf(buf, size+1, fmt, np);
unittest(strncmp(buf, expected, size) == 0 && (buf[size+1] == 0xff), "snprintf failed; size=%i fmt='%s' expected='%s' rslt='%s'\n",
size, fmt, expected, buf);
}
kfree(buf);
}
/* Check for missing cells property */
memset(&args, 0, sizeof(args));
EXPECT_BEGIN(KERN_INFO, "OF: /testcase-data/phandle-tests/consumer-a: could not get #phandle-cells-missing for /testcase-data/phandle-tests/provider1");
EXPECT_BEGIN(KERN_INFO, "OF: /testcase-data/phandle-tests/consumer-a: could not get #phandle-cells-missing for /testcase-data/phandle-tests/provider1");
/* Check for missing cells,map,mask property */
memset(&args, 0, sizeof(args));
EXPECT_BEGIN(KERN_INFO, "OF: /testcase-data/phandle-tests/consumer-b: could not get #phandle-missing-cells for /testcase-data/phandle-tests/provider1");
rc = of_parse_phandle_with_args_map(np, "phandle-list", "phandle-missing", 0, &args);
EXPECT_END(KERN_INFO, "OF: /testcase-data/phandle-tests/consumer-b: could not get #phandle-missing-cells for /testcase-data/phandle-tests/provider1");
unittest(!of_find_node_by_path("/testcase-data/changeset/n2/n21"), "'%pOF' still present after revert\n", n21);
unittest(of_property_present(parent, "prop-remove"), "failed to find removed prop after revert\n");
ret = of_property_read_string(parent, "prop-update", &propstr);
unittest(!ret, "failed to find updated prop after revert\n"); if (!ret)
unittest(strcmp(propstr, "hello") == 0, "original value not in updated property after revert");
for (i = 0; i < count; i++) {
ret = of_property_read_string_index(np, prop_name, i, &str); if (unittest(ret == 0, "failed to read %s[%d]\n", prop_name, i)) continue;
unittest(strcmp(str, expected_array[i]) == 0, "%s[%d] value mismatch (read '%s', exp '%s')\n",
prop_name, i, str, expected_array[i]);
}
}
for (i = 0; i < count; i++) {
ret = of_property_read_u32_index(np, prop_name, i, &val32); if (unittest(ret == 0, "failed to read %s[%d]\n", prop_name, i)) continue;
unittest(val32 == expected_array[i], "%s[%d] value mismatch (read '%u', exp '%u')\n",
prop_name, i, val32, expected_array[i]);
}
}
staticconststruct of_device_id match_node_table[] = {
{ .data = "A", .name = "name0", }, /* Name alone is lowest priority */
{ .data = "B", .type = "type1", }, /* followed by type alone */
{ .data = "Ca", .name = "name2", .type = "type1", }, /* followed by both together */
{ .data = "Cb", .name = "name2", }, /* Only match when type doesn't match */
{ .data = "Cc", .name = "name2", .type = "type2", },
/* Test that a parsing failure does not return -EPROBE_DEFER */
np = of_find_node_by_path("/testcase-data/testcase-device2");
pdev = of_find_device_by_node(np);
unittest(pdev, "device 2 creation failed\n");
EXPECT_BEGIN(KERN_INFO, "platform testcase-data:testcase-device2: error -ENXIO: IRQ index 0 not found");
irq = platform_get_irq(pdev, 0);
EXPECT_END(KERN_INFO, "platform testcase-data:testcase-device2: error -ENXIO: IRQ index 0 not found");
/* * Add a dummy resource to the test bus node after it is * registered to catch problems with un-inserted resources. The * DT code doesn't insert the resources, and it has caused the * kernel to oops in the past. This makes sure the same bug * doesn't crop up again.
*/
platform_device_add_resources(test_bus, &test_bus_res, 1);
of_platform_populate(np, match, NULL, &test_bus->dev);
for_each_child_of_node(np, child) {
for_each_child_of_node(child, grandchild) { if (!of_property_present(grandchild, "compatible")) continue;
pdev = of_find_device_by_node(grandchild);
unittest(pdev, "Could not create device for node '%pOFn'\n",
grandchild);
platform_device_put(pdev);
}
}
/** * update_node_properties - adds the properties * of np into dup node (present in live tree) and * updates parent of children of np to dup. * * @np: node whose properties are being added to the live tree * @dup: node present in live tree to be updated
*/ staticvoid update_node_properties(struct device_node *np, struct device_node *dup)
{ struct property *prop; struct property *save_next; struct device_node *child; int ret;
/* * "unittest internal error: unable to add testdata property" * * If this message reports a property in node '/__symbols__' then * the respective unittest overlay contains a label that has the * same name as a label in the live devicetree. The label will * be in the live devicetree only if the devicetree source was * compiled with the '-@' option. If you encounter this error, * please consider renaming __all__ of the labels in the unittest * overlay dts files with an odd prefix that is unlikely to be * used in a real devicetree.
*/
/* * open code for_each_property_of_node() because of_add_property() * sets prop->next to NULL
*/ for (prop = np->properties; prop != NULL; prop = save_next) {
save_next = prop->next;
ret = of_add_property(dup, prop); if (ret) { if (ret == -EEXIST && !strcmp(prop->name, "name")) continue;
pr_err("unittest internal error: unable to add testdata property %pOF/%s",
np, prop->name);
}
}
}
/** * attach_node_and_children - attaches nodes * and its children to live tree. * CAUTION: misleading function name - if node @np already exists in * the live tree then children of @np are *not* attached to the live * tree. This works for the current test devicetree nodes because such * nodes do not have child nodes. * * @np: Node to attach to live tree
*/ staticvoid attach_node_and_children(struct device_node *np)
{ struct device_node *next, *dup, *child; unsignedlong flags; constchar *full_name;
full_name = kasprintf(GFP_KERNEL, "%pOF", np); if (!full_name) return;
if (!strcmp(full_name, "/__local_fixups__") ||
!strcmp(full_name, "/__fixups__")) {
kfree(full_name); return;
}
while (child) {
next = child->sibling;
attach_node_and_children(child);
child = next;
}
}
/** * unittest_data_add - Reads, copies data from * linked tree and attaches it to the live tree
*/ staticint __init unittest_data_add(void)
{ void *unittest_data; void *unittest_data_align; struct device_node *unittest_data_node = NULL, *np; /* * __dtbo_testcases_begin[] and __dtbo_testcases_end[] are magically * created by cmd_wrap_S_dtbo in scripts/Makefile.dtbs
*/ extern uint8_t __dtbo_testcases_begin[]; extern uint8_t __dtbo_testcases_end[]; constint size = __dtbo_testcases_end - __dtbo_testcases_begin; int rc; void *ret;
if (!size) {
pr_warn("%s: testcases is empty\n", __func__); return -ENODATA;
}
ret = of_fdt_unflatten_tree(unittest_data_align, NULL, &unittest_data_node); if (!ret) {
pr_warn("%s: unflatten testcases tree failed\n", __func__);
kfree(unittest_data); return -ENODATA;
} if (!unittest_data_node) {
pr_warn("%s: testcases tree is empty\n", __func__);
kfree(unittest_data); return -ENODATA;
}
/* * This lock normally encloses of_resolve_phandles()
*/
of_overlay_mutex_lock();
rc = of_resolve_phandles(unittest_data_node); if (rc) {
pr_err("%s: Failed to resolve phandles (rc=%i)\n", __func__, rc);
rc = -EINVAL; goto unlock;
}
/* attach the sub-tree to live tree */ if (!of_root) {
pr_warn("%s: no live tree to attach sub-tree\n", __func__);
kfree(unittest_data);
rc = -ENODEV; goto unlock;
}
EXPECT_BEGIN(KERN_INFO, "Duplicate name in testcase-data, renamed to \"duplicate-name#1\"");
staticvoid __init of_unittest_overlay_gpio(void)
{ int chip_request_count; int probe_pass_count; int ret;
/* * tests: apply overlays before registering driver * Similar to installing a driver as a module, the * driver is registered after applying the overlays. * * The overlays are applied by overlay_data_apply() * instead of of_unittest_apply_overlay() so that they * will not be tracked. Thus they will not be removed * by of_unittest_remove_tracked_overlays(). * * - apply overlay_gpio_01 * - apply overlay_gpio_02a * - apply overlay_gpio_02b * - register driver * * register driver will result in * - probe and processing gpio hog for overlay_gpio_01 * - probe for overlay_gpio_02a * - processing gpio for overlay_gpio_02b
*/
ret = platform_driver_register(&unittest_gpio_driver); if (unittest(ret == 0, "could not register unittest gpio driver\n")) return;
unittest(probe_pass_count + 2 == unittest_gpio_probe_pass_count, "unittest_gpio_probe() failed or not called\n");
unittest(chip_request_count + 2 == unittest_gpio_chip_request_count, "unittest_gpio_chip_request() called %d times (expected 1 time)\n",
unittest_gpio_chip_request_count - chip_request_count);
/* * tests: apply overlays after registering driver * * Similar to a driver built-in to the kernel, the * driver is registered before applying the overlays. * * overlay_gpio_03 contains gpio node and child gpio hog node * * - apply overlay_gpio_03 * * apply overlay will result in * - probe and processing gpio hog.
*/
staticvoid of_unittest_untrack_overlay(int ovcs_id)
{ if (WARN_ON(track_ovcs_id_cnt < 1)) return;
track_ovcs_id_cnt--;
/* If out of synch then test is broken. Do not try to recover. */
WARN_ON(track_ovcs_id[track_ovcs_id_cnt] != ovcs_id);
}
staticvoid of_unittest_remove_tracked_overlays(void)
{ int ret, ovcs_id, overlay_nr, save_ovcs_id; constchar *overlay_name;
while (track_ovcs_id_cnt > 0) {
ovcs_id = track_ovcs_id[track_ovcs_id_cnt - 1];
overlay_nr = track_ovcs_id_overlay_nr[track_ovcs_id_cnt - 1];
save_ovcs_id = ovcs_id;
ret = of_overlay_remove(&ovcs_id); if (ret == -ENODEV) {
overlay_name = overlay_name_from_nr(overlay_nr);
pr_warn("%s: of_overlay_remove() for overlay \"%s\" failed, ret = %d\n",
__func__, overlay_name, ret);
}
of_unittest_untrack_overlay(save_ovcs_id);
}
}
staticint __init of_unittest_apply_overlay(int overlay_nr, int *ovcs_id)
{ /* * The overlay will be tracked, thus it will be removed * by of_unittest_remove_tracked_overlays().
*/
constchar *overlay_name;
overlay_name = overlay_name_from_nr(overlay_nr);
if (!overlay_data_apply(overlay_name, ovcs_id)) {
unittest(0, "could not apply overlay \"%s\"\n", overlay_name); return -EFAULT;
}
of_unittest_track_overlay(*ovcs_id, overlay_nr);
return 0;
}
staticint __init __of_unittest_apply_overlay_check(int overlay_nr, int unittest_nr, int before, int after, enum overlay_type ovtype)
{ int ret, ovcs_id;
/* unittest device must be in before state */ if (of_unittest_device_exists(unittest_nr, ovtype) != before) {
unittest(0, "%s with device @\"%s\" %s\n",
overlay_name_from_nr(overlay_nr),
unittest_path(unittest_nr, ovtype),
!before ? "enabled" : "disabled"); return -EINVAL;
}
/* apply the overlay */
ovcs_id = 0;
ret = of_unittest_apply_overlay(overlay_nr, &ovcs_id); if (ret != 0) { /* of_unittest_apply_overlay already called unittest() */ return ret;
}
/* unittest device must be in after state */ if (of_unittest_device_exists(unittest_nr, ovtype) != after) {
unittest(0, "%s with device @\"%s\" %s\n",
overlay_name_from_nr(overlay_nr),
unittest_path(unittest_nr, ovtype),
!after ? "enabled" : "disabled"); return -EINVAL;
}
return ovcs_id;
}
/* apply an overlay while checking before and after states */ staticint __init of_unittest_apply_overlay_check(int overlay_nr, int unittest_nr, int before, int after, enum overlay_type ovtype)
{ int ovcs_id = __of_unittest_apply_overlay_check(overlay_nr,
unittest_nr, before, after, ovtype); if (ovcs_id < 0) return ovcs_id;
return 0;
}
/* apply an overlay and then revert it while checking before, after states */ staticint __init of_unittest_apply_revert_overlay_check(int overlay_nr, int unittest_nr, int before, int after, enum overlay_type ovtype)
{ int ret, ovcs_id, save_ovcs_id;
/* remove the overlay */
save_ovcs_id = ovcs_id;
ret = of_overlay_remove(&ovcs_id); if (ret != 0) {
unittest(0, "%s failed to be destroyed @\"%s\"\n",
overlay_name_from_nr(overlay_nr),
unittest_path(unittest_nr, ovtype)); return ret;
}
of_unittest_untrack_overlay(save_ovcs_id);
/* unittest device must be again in before state */ if (of_unittest_device_exists(unittest_nr, ovtype) != before) {
unittest(0, "%s with device @\"%s\" %s\n",
overlay_name_from_nr(overlay_nr),
unittest_path(unittest_nr, ovtype),
!before ? "enabled" : "disabled"); return -EINVAL;
}
return 0;
}
/* test activation of device */ staticvoid __init of_unittest_overlay_0(void)
{ int ret;
EXPECT_BEGIN(KERN_INFO, "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data/overlay-node/test-bus/test-unittest0/status");
/* device should enable */
ret = of_unittest_apply_overlay_check(0, 0, 0, 1, PDEV_OVERLAY);
EXPECT_END(KERN_INFO, "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data/overlay-node/test-bus/test-unittest0/status");
if (ret) return;
unittest(1, "overlay test %d passed\n", 0);
}
/* test deactivation of device */ staticvoid __init of_unittest_overlay_1(void)
{ int ret;
EXPECT_BEGIN(KERN_INFO, "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data/overlay-node/test-bus/test-unittest1/status");
/* device should disable */
ret = of_unittest_apply_overlay_check(1, 1, 1, 0, PDEV_OVERLAY);
EXPECT_END(KERN_INFO, "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data/overlay-node/test-bus/test-unittest1/status");
if (ret) return;
unittest(1, "overlay test %d passed\n", 1);
}
/* test activation of device */ staticvoid __init of_unittest_overlay_2(void)
{ int ret;
EXPECT_BEGIN(KERN_INFO, "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data/overlay-node/test-bus/test-unittest2/status");
/* device should enable */
ret = of_unittest_apply_overlay_check(2, 2, 0, 1, PDEV_OVERLAY);
EXPECT_END(KERN_INFO, "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data/overlay-node/test-bus/test-unittest2/status");
if (ret) return;
unittest(1, "overlay test %d passed\n", 2);
}
/* test deactivation of device */ staticvoid __init of_unittest_overlay_3(void)
{ int ret;
EXPECT_BEGIN(KERN_INFO, "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data/overlay-node/test-bus/test-unittest3/status");
/* device should disable */
ret = of_unittest_apply_overlay_check(3, 3, 1, 0, PDEV_OVERLAY);
EXPECT_END(KERN_INFO, "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data/overlay-node/test-bus/test-unittest3/status");
if (ret) return;
unittest(1, "overlay test %d passed\n", 3);
}
/* test activation of a full device node */ staticvoid __init of_unittest_overlay_4(void)
{ /* device should disable */ if (of_unittest_apply_overlay_check(4, 4, 0, 1, PDEV_OVERLAY)) return;
unittest(1, "overlay test %d passed\n", 4);
}
/* test overlay apply/revert sequence */ staticvoid __init of_unittest_overlay_5(void)
{ int ret;
EXPECT_BEGIN(KERN_INFO, "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data/overlay-node/test-bus/test-unittest5/status");
/* device should disable */
ret = of_unittest_apply_revert_overlay_check(5, 5, 0, 1, PDEV_OVERLAY);
EXPECT_END(KERN_INFO, "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data/overlay-node/test-bus/test-unittest5/status");
if (ret) return;
unittest(1, "overlay test %d passed\n", 5);
}
/* test overlay application in sequence */ staticvoid __init of_unittest_overlay_6(void)
{ int i, save_ovcs_id[2], ovcs_id; int overlay_nr = 6, unittest_nr = 6; int before = 0, after = 1; constchar *overlay_name;
int ret;
/* unittest device must be in before state */ for (i = 0; i < 2; i++) { if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY)
!= before) {
unittest(0, "%s with device @\"%s\" %s\n",
overlay_name_from_nr(overlay_nr + i),
unittest_path(unittest_nr + i,
PDEV_OVERLAY),
!before ? "enabled" : "disabled"); return;
}
}
/* apply the overlays */
EXPECT_BEGIN(KERN_INFO, "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data/overlay-node/test-bus/test-unittest6/status");
if (!ret) {
unittest(0, "could not apply overlay \"%s\"\n", overlay_name); return;
}
save_ovcs_id[1] = ovcs_id;
of_unittest_track_overlay(ovcs_id, overlay_nr + 1);
EXPECT_END(KERN_INFO, "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data/overlay-node/test-bus/test-unittest7/status");
for (i = 0; i < 2; i++) { /* unittest device must be in after state */ if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY)
!= after) {
unittest(0, "overlay @\"%s\" failed @\"%s\" %s\n",
overlay_name_from_nr(overlay_nr + i),
unittest_path(unittest_nr + i,
PDEV_OVERLAY),
!after ? "enabled" : "disabled"); return;
}
}
for (i = 1; i >= 0; i--) {
ovcs_id = save_ovcs_id[i]; if (of_overlay_remove(&ovcs_id)) {
unittest(0, "%s failed destroy @\"%s\"\n",
overlay_name_from_nr(overlay_nr + i),
unittest_path(unittest_nr + i,
PDEV_OVERLAY)); return;
}
of_unittest_untrack_overlay(save_ovcs_id[i]);
}
for (i = 0; i < 2; i++) { /* unittest device must be again in before state */ if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY)
!= before) {
unittest(0, "%s with device @\"%s\" %s\n",
overlay_name_from_nr(overlay_nr + i),
unittest_path(unittest_nr + i,
PDEV_OVERLAY),
!before ? "enabled" : "disabled"); return;
}
}
unittest(1, "overlay test %d passed\n", 6);
}
/* test overlay application in sequence */ staticvoid __init of_unittest_overlay_8(void)
{ int i, save_ovcs_id[2], ovcs_id; int overlay_nr = 8, unittest_nr = 8; constchar *overlay_name; int ret;
/* we don't care about device state in this test */
EXPECT_BEGIN(KERN_INFO, "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data/overlay-node/test-bus/test-unittest8/status");
/* now try to remove first overlay (it should fail) */
ovcs_id = save_ovcs_id[0];
EXPECT_BEGIN(KERN_INFO, "OF: overlay: node_overlaps_later_cs: #6 overlaps with #7 @/testcase-data/overlay-node/test-bus/test-unittest8");
EXPECT_BEGIN(KERN_INFO, "OF: overlay: overlay #6 is not topmost");
ret = of_overlay_remove(&ovcs_id);
EXPECT_END(KERN_INFO, "OF: overlay: overlay #6 is not topmost");
EXPECT_END(KERN_INFO, "OF: overlay: node_overlaps_later_cs: #6 overlaps with #7 @/testcase-data/overlay-node/test-bus/test-unittest8");
if (!ret) { /* * Should never get here. If we do, expect a lot of * subsequent tracking and overlay removal related errors.
*/
unittest(0, "%s was destroyed @\"%s\"\n",
overlay_name_from_nr(overlay_nr + 0),
unittest_path(unittest_nr,
PDEV_OVERLAY)); return;
}
/* removing them in order should work */ for (i = 1; i >= 0; i--) {
ovcs_id = save_ovcs_id[i]; if (of_overlay_remove(&ovcs_id)) {
unittest(0, "%s not destroyed @\"%s\"\n",
overlay_name_from_nr(overlay_nr + i),
unittest_path(unittest_nr,
PDEV_OVERLAY)); return;
}
of_unittest_untrack_overlay(save_ovcs_id[i]);
}
unittest(1, "overlay test %d passed\n", 8);
}
/* test insertion of a bus with parent devices */ staticvoid __init of_unittest_overlay_10(void)
{ int ret; char *child_path;
/* device should disable */
ret = of_unittest_apply_overlay_check(10, 10, 0, 1, PDEV_OVERLAY);
if (unittest(ret == 0, "overlay test %d failed; overlay application\n", 10)) return;
child_path = kasprintf(GFP_KERNEL, "%s/test-unittest101",
unittest_path(10, PDEV_OVERLAY)); if (unittest(child_path, "overlay test %d failed; kasprintf\n", 10)) return;
ret = of_path_device_type_exists(child_path, PDEV_OVERLAY);
kfree(child_path);
unittest(ret, "overlay test %d failed; no child device\n", 10);
}
/* test insertion of a bus with parent devices (and revert) */ staticvoid __init of_unittest_overlay_11(void)
{ int ret;
/* device should disable */
ret = of_unittest_apply_revert_overlay_check(11, 11, 0, 1,
PDEV_OVERLAY);
/* * For overlay_16 .. overlay_19, check that returning an error * works for each of the actions by setting an arbitrary return * error number that matches the test number. e.g. for unittest16, * ret = -EBUSY which is -16. * * OVERLAY_INFO() for the overlays is declared to expect the same * error number, so overlay_data_apply() will return no error. * * overlay_20 will return NOTIFY_DONE
*/
ret = 0;
of_node_get(nd->overlay);
switch (action) {
case OF_OVERLAY_PRE_APPLY:
found = of_find_node_by_name(nd->overlay, "test-unittest16"); if (found) {
of_node_put(found);
ret = -EBUSY;
} break;
case OF_OVERLAY_POST_APPLY:
found = of_find_node_by_name(nd->overlay, "test-unittest17"); if (found) {
of_node_put(found);
ret = -EEXIST;
} break;
case OF_OVERLAY_PRE_REMOVE:
found = of_find_node_by_name(nd->overlay, "test-unittest18"); if (found) {
of_node_put(found);
ret = -EXDEV;
} break;
case OF_OVERLAY_POST_REMOVE:
found = of_find_node_by_name(nd->overlay, "test-unittest19"); if (found) {
of_node_put(found);
ret = -ENODEV;
} break;
default: /* should not happen */
of_node_put(nd->overlay);
ret = -EINVAL; break;
}
staticvoid __init of_unittest_overlay_notify(void)
{ int ovcs_id; int ret;
ret = of_overlay_notifier_register(&of_nb);
unittest(!ret, "of_overlay_notifier_register() failed, ret = %d\n", ret); if (ret) return;
/* * The overlays are applied by overlay_data_apply() * instead of of_unittest_apply_overlay() so that they * will not be tracked. Thus they will not be removed * by of_unittest_remove_tracked_overlays(). * * Applying overlays 16 - 19 will each trigger an error for a * different action in of_notify(). * * Applying overlay 20 will not trigger any error in of_notify().
*/
unittest(ovcs_id, "ovcs_id not created for overlay_19\n");
if (ovcs_id) {
EXPECT_BEGIN(KERN_INFO, "OF: overlay: overlay changeset post-remove notifier error -19, target: /testcase-data/overlay-node/test-bus");
ret = of_overlay_remove(&ovcs_id);
EXPECT_END(KERN_INFO, "OF: overlay: overlay changeset post-remove notifier error -19, target: /testcase-data/overlay-node/test-bus"); if (ret == -ENODEV)
unittest(1, "overlay_19 of_overlay_remove() injected error for OF_OVERLAY_POST_REMOVE\n"); else
unittest(0, "overlay_19 of_overlay_remove() injected error for OF_OVERLAY_POST_REMOVE not returned\n");
} else {
unittest(1, "ovcs_id removed for overlay_19\n");
}
unittest(!ovcs_id, "changeset ovcs_id = %d not removed for overlay_19\n",
ovcs_id);
/* --- overlay 20 --- */
unittest(overlay_data_apply("overlay_20", &ovcs_id), "overlay notify no injected error\n");
if (ovcs_id) {
ret = of_overlay_remove(&ovcs_id); if (ret)
unittest(1, "overlay_20 failed to be destroyed, ret = %d\n",
ret);
} else {
unittest(1, "ovcs_id not created for overlay_20\n");
}
unittest(!of_overlay_notifier_unregister(&of_nb), "of_overlay_notifier_unregister() failed, ret = %d\n", ret);
}
/* * Node lifecycle tests, non-dynamic node: * * - Decrementing refcount to zero via of_node_put() should cause the * attempt to free the node memory by of_node_release() to fail * because the node is not a dynamic node. * * - Decrementing refcount past zero should result in additional * errors reported.
*/
EXPECT_BEGIN(KERN_INFO, "OF: ERROR: of_node_release() detected bad of_node_put() on /testcase-data/refcount-node");
/* * refcount is now one, decrementing to zero will result in a call to * of_node_release() to free the node's memory, which should result * in an error
*/
unittest(1, "/testcase-data/refcount-node is one");
of_node_put(np);
EXPECT_END(KERN_INFO, "OF: ERROR: of_node_release() detected bad of_node_put() on /testcase-data/refcount-node");
/* * expect stack trace for subsequent of_node_put(): * __refcount_sub_and_test() calls: * refcount_warn_saturate(r, REFCOUNT_SUB_UAF) * * Not capturing entire WARN_ONCE() trace with EXPECT_*(), just * the first three lines, and the last line.
*/
EXPECT_BEGIN(KERN_INFO, "------------[ cut here ]------------");
EXPECT_BEGIN(KERN_INFO, "WARNING: <<all>>");
EXPECT_BEGIN(KERN_INFO, "refcount_t: underflow; use-after-free.");
EXPECT_BEGIN(KERN_INFO, "---[ end trace <<int>> ]---");
/* refcount is now zero, this should fail */
unittest(1, "/testcase-data/refcount-node is zero");
of_node_put(np);
EXPECT_END(KERN_INFO, "---[ end trace <<int>> ]---");
EXPECT_END(KERN_INFO, "refcount_t: underflow; use-after-free.");
EXPECT_END(KERN_INFO, "WARNING: <<all>>");
EXPECT_END(KERN_INFO, "------------[ cut here ]------------");
/* * Q. do we expect to get yet another warning? * A. no, the WARNING is from WARN_ONCE()
*/
EXPECT_NOT_BEGIN(KERN_INFO, "------------[ cut here ]------------");
EXPECT_NOT_BEGIN(KERN_INFO, "WARNING: <<all>>");
EXPECT_NOT_BEGIN(KERN_INFO, "refcount_t: underflow; use-after-free.");
EXPECT_NOT_BEGIN(KERN_INFO, "---[ end trace <<int>> ]---");
unittest(1, "/testcase-data/refcount-node is zero, second time");
of_node_put(np);
EXPECT_NOT_END(KERN_INFO, "---[ end trace <<int>> ]---");
EXPECT_NOT_END(KERN_INFO, "refcount_t: underflow; use-after-free.");
EXPECT_NOT_END(KERN_INFO, "WARNING: <<all>>");
EXPECT_NOT_END(KERN_INFO, "------------[ cut here ]------------");
/* * refcount of zero will trigger stack traces from any further * attempt to of_node_get() node "refcount-node". One example of * this is where of_unittest_check_node_linkage() will recursively * scan the tree, with 'for_each_child_of_node()' doing an * of_node_get() of the children of a node. * * Prevent the stack trace by removing node "refcount-node" from * its parent's child list. * * WARNING: EVIL, EVIL, EVIL: * * Directly manipulate the child list of node /testcase-data to * remove child refcount-node. This is ignoring all proper methods * of removing a child and will leak a small amount of memory.
*/
np = of_find_node_by_path(refcount_parent_path);
unittest(np, "find refcount_parent_path \"%s\"\n", refcount_parent_path);
unittest(np, "ERROR: devicetree live tree left in a 'bad state' if test fail\n"); if (np == NULL) return;
/* * Create base device tree for the overlay unittest. * * This is called from very early boot code. * * Do as much as possible the same way as done in __unflatten_device_tree * and other early boot steps for the normal FDT so that the overlay base * unflattened tree will have the same characteristics as the real tree * (such as having memory allocated by the early allocator). The goal * is to test "the real thing" as much as possible, and test "test setup * code" as little as possible. * * Have to stop before resolving phandles, because that uses kmalloc.
*/ void __init unittest_unflatten_overlay_base(void)
{ struct overlay_info *info;
u32 data_size; void *new_fdt;
u32 size; int found = 0; constchar *overlay_name = "overlay_base";
for (info = overlays; info && info->name; info++) { if (!strcmp(overlay_name, info->name)) {
found = 1; break;
}
} if (!found) {
pr_err("no overlay data for %s\n", overlay_name); return;
}
info = &overlays[0];
if (info->expected_result != -9999) {
pr_err("No dtb 'overlay_base' to attach\n"); return;
}
data_size = info->dtbo_end - info->dtbo_begin; if (!data_size) {
pr_err("No dtb 'overlay_base' to attach\n"); return;
}
size = fdt_totalsize(info->dtbo_begin); if (size != data_size) {
pr_err("dtb 'overlay_base' header totalsize != actual size"); return;
}
new_fdt = dt_alloc_memory(size, roundup_pow_of_two(FDT_V17_SIZE)); if (!new_fdt) {
pr_err("alloc for dtb 'overlay_base' failed"); return;
}
/* * The purpose of of_unittest_overlay_data_add is to add an * overlay in the normal fashion. This is a test of the whole * picture, instead of testing individual elements. * * A secondary purpose is to be able to verify that the contents of * /proc/device-tree/ contains the updated structure and values from * the overlay. That must be verified separately in user space. * * Return 0 on unexpected error.
*/ staticint __init overlay_data_apply(constchar *overlay_name, int *ovcs_id)
{ struct overlay_info *info; int passed = 1; int found = 0; int ret, ret2;
u32 size;
for (info = overlays; info && info->name; info++) { if (!strcmp(overlay_name, info->name)) {
found = 1; break;
}
} if (!found) {
pr_err("no overlay data for %s\n", overlay_name); return 0;
}
size = info->dtbo_end - info->dtbo_begin; if (!size)
pr_err("no overlay data for %s\n", overlay_name);
ret = of_overlay_fdt_apply(info->dtbo_begin, size, &info->ovcs_id,
NULL); if (ovcs_id)
*ovcs_id = info->ovcs_id; if (ret < 0) goto out;
if (ret < 0) { /* changeset may be partially applied */
ret2 = of_overlay_remove(&info->ovcs_id); if (ret2 != info->expected_result_remove) {
pr_err("of_overlay_remove() expected %d, ret=%d, %s\n",
info->expected_result_remove, ret2,
overlay_name);
passed = 0;
}
}
return passed;
}
/* * The purpose of of_unittest_overlay_high_level is to add an overlay * in the normal fashion. This is a test of the whole picture, * instead of individual elements. * * The first part of the function is _not_ normal overlay usage; it is * finishing splicing the base overlay device tree into the live tree.
*/ static __init void of_unittest_overlay_high_level(void)
{ struct device_node *last_sibling; struct device_node *np; struct device_node *of_symbols; struct device_node *overlay_base_symbols; struct device_node **pprev; struct property *prop; int ret;
if (!overlay_base_root) {
unittest(0, "overlay_base_root not initialized\n"); return;
}
/* * Could not fixup phandles in unittest_unflatten_overlay_base() * because kmalloc() was not yet available.
*/
of_overlay_mutex_lock();
of_resolve_phandles(overlay_base_root);
of_overlay_mutex_unlock();
/* * do not allow overlay_base to duplicate any node already in * tree, this greatly simplifies the code
*/
/* * remove overlay_base_root node "__local_fixups", after * being used by of_resolve_phandles()
*/
pprev = &overlay_base_root->child; for (np = overlay_base_root->child; np; np = np->sibling) { if (of_node_name_eq(np, "__local_fixups__")) {
*pprev = np->sibling; break;
}
pprev = &np->sibling;
}
/* remove overlay_base_root node "__symbols__" if in live tree */
of_symbols = of_get_child_by_name(of_root, "__symbols__"); if (of_symbols) { /* will have to graft properties from node into live tree */
pprev = &overlay_base_root->child; for (np = overlay_base_root->child; np; np = np->sibling) { if (of_node_name_eq(np, "__symbols__")) {
overlay_base_symbols = np;
*pprev = np->sibling; break;
}
pprev = &np->sibling;
}
}
for_each_child_of_node(overlay_base_root, np) { struct device_node *base_child;
for_each_child_of_node(of_root, base_child) { if (!strcmp(np->full_name, base_child->full_name)) {
unittest(0, "illegal node name in overlay_base %pOFn",
np);
of_node_put(np);
of_node_put(base_child); return;
}
}
}
/* * overlay 'overlay_base' is not allowed to have root * properties, so only need to splice nodes into main device tree. * * root node of *overlay_base_root will not be freed, it is lost * memory.
*/
dn = pdev->dev.of_node; if (!dn) {
dev_err(&pdev->dev, "does not find bus endpoint"); return -EINVAL;
}
for (info = overlays; info && info->name; info++) { if (!strcmp(info->name, "overlay_pci_node")) break;
} if (!info || !info->name) {
dev_err(&pdev->dev, "no overlay data for overlay_pci_node"); return -ENODEV;
}
size = info->dtbo_end - info->dtbo_begin;
ret = of_overlay_fdt_apply(info->dtbo_begin, size, &ovcs_id, dn);
of_node_put(dn); if (ret) return ret;
if (!IS_ENABLED(CONFIG_PCI_DYNAMIC_OF_NODES)) return;
rc = pci_register_driver(&testdrv_driver);
unittest(!rc, "Failed to register pci test driver; rc = %d\n", rc); if (rc) return;
rc = platform_driver_register(&unittest_pci_driver); if (unittest(!rc, "Failed to register unittest pci driver\n")) {
pci_unregister_driver(&testdrv_driver); return;
}
while ((pdev = pci_get_device(PCI_VENDOR_ID_REDHAT, 0x5, pdev)) != NULL) {
of_unittest_pci_node_verify(pdev, true);
of_unittest_pci_dev_num++;
} if (pdev)
pci_dev_put(pdev);
unittest(of_unittest_pci_dev_num, "No test PCI device been found. Please run QEMU with '-device pci-testdev'\n");
unittest(of_unittest_pci_dev_num == of_unittest_pci_child_num, "Child device number %d is not expected %d", of_unittest_pci_child_num,
of_unittest_pci_dev_num);
staticint __init of_unittest(void)
{ struct device_node *np; int res;
pr_info("start of unittest - you will see error messages\n");
/* Taint the kernel so we know we've run tests. */
add_taint(TAINT_TEST, LOCKDEP_STILL_OK);
/* adding data for unittest */
res = unittest_data_add(); if (res) return res; if (!of_aliases)
of_aliases = of_find_node_by_path("/aliases");
np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a"); if (!np) {
pr_info("No testcase data in device tree; not running tests\n"); return 0;
}
of_node_put(np);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.