Merge tag 'firewire-updates-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394

Pull firewire updates from Takashi Sakamoto:
 "Error handling, a potential bug fix, and KUnit tests:

   - Handle failures when generating the contents of the configuration
     ROM with parameters supplied by in-kernel implementations such as
     unit drivers (Sreeraj S Kurup)

   - Fix potential memory leak when an invalid self-ID sequence causes
     an error while building the internal node tree (Abdun Nihaal)

     KUnit tests have been added to trigger this case"

* tag 'firewire-updates-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394:
  firewire: core: fix memory leak in error path of build_tree()
  firewire: core: validate parent port count before allocating nodes in build_tree()
  firewire: core: consolidate port counting in build_tree()
  firewire: core: add KUnit tests for failure of tree building
  firewire: core: add KUnit tests for successful tree building
  firewire: core: add KUnit test skeleton for node tree
  firewire: core: validate sub-block lengths in fw_core_add_descriptor()
  firewire: core: validate overall descriptor length in fw_core_add_descriptor()
This commit is contained in:
Linus Torvalds
2026-08-22 08:41:28 -07:00
5 changed files with 687 additions and 30 deletions
+1
View File
@@ -6,3 +6,4 @@ CONFIG_FIREWIRE_KUNIT_DEVICE_ATTRIBUTE_TEST=y
CONFIG_FIREWIRE_KUNIT_PACKET_SERDES_TEST=y
CONFIG_FIREWIRE_KUNIT_SELF_ID_SEQUENCE_HELPER_TEST=y
CONFIG_FIREWIRE_KUNIT_OHCI_SERDES_TEST=y
CONFIG_FIREWIRE_KUNIT_NODE_TREE_TEST=y
+15
View File
@@ -81,6 +81,21 @@ config FIREWIRE_KUNIT_SELF_ID_SEQUENCE_HELPER_TEST
For more information on KUnit and unit tests in general, refer
to the KUnit documentation in Documentation/dev-tools/kunit/.
config FIREWIRE_KUNIT_NODE_TREE_TEST
tristate "KUnit tests for node tree" if !KUNIT_ALL_TESTS
depends on FIREWIRE && KUNIT
default KUNIT_ALL_TESTS
help
This builds the KUnit tests for node tree.
KUnit tests run during boot and output the results to the debug
log in TAP format (https://testanything.org/). Only useful for
kernel devs running KUnit test harness and are not for inclusion
into a production build.
For more information on KUnit and unit tests in general, refer
to the KUnit documentation in Documentation/dev-tools/kunit/.
config FIREWIRE_OHCI
tristate "OHCI-1394 controllers"
depends on PCI && FIREWIRE
+24 -8
View File
@@ -16,6 +16,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/minmax.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
@@ -167,15 +168,30 @@ int fw_core_add_descriptor(struct fw_descriptor *desc)
{
size_t i;
/*
* Check descriptor is valid; the length of all blocks in the
* descriptor has to add up to exactly the length of the
* block.
*/
i = 0;
while (i < desc->length)
i += (desc->data[i] >> 16) + 1;
/* Reject empty descriptors or those exceeding max Config ROM size (256 quadlets) */
if (!in_range(desc->length, 1, 256))
return -EINVAL;
i = 0;
/*
* Validate internal block structures within the descriptor. Each sub-block
* encodes its length in the top 16 bits of its header quadlet.
*/
while (i < desc->length) {
u16 block_len = desc->data[i] >> 16;
/*
* Guard against corrupted descriptors where an individual block length
* claims to extend past the allocated end of desc->data, avoiding
* out-of-bounds reads.
*/
if (block_len >= desc->length - i)
return -EINVAL;
i += block_len + 1;
}
/* The sum of sub-block lengths must match total descriptor length */
if (i != desc->length)
return -EINVAL;
+40 -22
View File
@@ -88,6 +88,17 @@ static inline struct fw_node *fw_node(struct list_head *l)
return list_entry(l, struct fw_node, link);
}
typedef void (*fw_node_callback_t)(struct fw_card *card, struct fw_node *node,
struct fw_node *parent);
static void for_each_fw_node(struct fw_card *card, struct fw_node *root,
fw_node_callback_t callback);
static void free_fw_node(struct fw_card *card, struct fw_node *node, struct fw_node *parent)
{
kfree(node);
}
/*
* This function builds the tree representation of the topology given
* by the self IDs from the latest bus reset. During the construction
@@ -119,8 +130,8 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self
while (enumerator.quadlet_count > 0) {
unsigned int child_port_count = 0;
unsigned int parent_port_count = 0;
unsigned int total_port_count = 0;
unsigned int parent_count = 0;
unsigned int quadlet_count;
const u32 *self_id_sequence;
unsigned int port_capacity;
@@ -134,7 +145,7 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self
if (PTR_ERR(self_id_sequence) != -ENODATA) {
fw_err(card, "inconsistent extended self IDs: %ld\n",
PTR_ERR(self_id_sequence));
return NULL;
goto error;
}
break;
}
@@ -148,26 +159,38 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self
switch (port_status) {
case PHY_PACKET_SELF_ID_PORT_STATUS_CHILD:
++child_port_count;
fallthrough;
break;
case PHY_PACKET_SELF_ID_PORT_STATUS_PARENT:
++parent_port_count;
break;
case PHY_PACKET_SELF_ID_PORT_STATUS_NCONN:
++total_port_count;
fallthrough;
break;
case PHY_PACKET_SELF_ID_PORT_STATUS_NONE:
default:
break;
}
}
total_port_count += child_port_count + parent_port_count;
// Check that the node reports exactly one parent port, except for the root, which
// of course should have no parents.
if ((enumerator.quadlet_count == 0 && parent_port_count != 0) ||
(enumerator.quadlet_count > 0 && parent_port_count != 1)) {
fw_err(card, "parent port inconsistency for node %d: parent_count=%d\n",
phy_id, parent_port_count);
goto error;
}
if (phy_id != phy_packet_self_id_get_phy_id(self_id_sequence[0])) {
fw_err(card, "PHY ID mismatch in self ID: %d != %d\n",
phy_id, phy_packet_self_id_get_phy_id(self_id_sequence[0]));
return NULL;
goto error;
}
if (child_port_count > stack_depth) {
fw_err(card, "topology stack underflow\n");
return NULL;
goto error;
}
/*
@@ -185,7 +208,7 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self
node = fw_node_create(self_id_sequence[0], total_port_count, card->color);
if (node == NULL) {
fw_err(card, "out of memory while building topology\n");
return NULL;
goto error;
}
if (phy_id == (card->node_id & 0x3f))
@@ -203,7 +226,6 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self
// we temporarily abuse node->color for remembering the entry in
// the node->ports array where the parent node should be. Later,
// when we handle the parent node, we fix up the reference.
++parent_count;
node->color = port_index;
break;
@@ -221,21 +243,12 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self
}
}
// Check that the node reports exactly one parent port, except for the root, which
// of course should have no parents.
if ((enumerator.quadlet_count == 0 && parent_count != 0) ||
(enumerator.quadlet_count > 0 && parent_count != 1)) {
fw_err(card, "parent port inconsistency for node %d: "
"parent_count=%d\n", phy_id, parent_count);
return NULL;
}
/* Pop the child nodes off the stack and push the new node. */
__list_del(h->prev, &stack);
list_add_tail(&node->link, &stack);
stack_depth += 1 - child_port_count;
if (node->phy_speed == SCODE_BETA && parent_count + child_port_count > 1)
if (node->phy_speed == SCODE_BETA && parent_port_count + child_port_count > 1)
beta_repeaters_present = true;
// If PHYs report different gap counts, set an invalid count which will force a gap
@@ -254,12 +267,13 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self
card->beta_repeaters_present = beta_repeaters_present;
return local_node;
error:
++card->color;
list_for_each_entry_safe(node, child, &stack, link)
for_each_fw_node(card, node, free_fw_node);
return NULL;
}
typedef void (*fw_node_callback_t)(struct fw_card * card,
struct fw_node * node,
struct fw_node * parent);
static void for_each_fw_node(struct fw_card *card, struct fw_node *root,
fw_node_callback_t callback)
{
@@ -507,3 +521,7 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation,
}
}
EXPORT_SYMBOL(fw_core_handle_bus_reset);
#ifdef CONFIG_FIREWIRE_KUNIT_NODE_TREE_TEST
#include "node-tree-test.c"
#endif
+607
View File
@@ -0,0 +1,607 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// node-tree-test.c - An application of Kunit to test node tree.
//
// Copyright (c) 2026 Takashi Sakamoto
//
// This file can not be built independently since it is intentionally included in core-topology.c.
#include <kunit/test.h>
#include <kunit/test-bug.h>
#include <kunit/device.h>
struct private_data {
struct fw_card *card;
unsigned int release_count;
};
static int node_tree_test_init(struct kunit *test)
{
struct private_data *data;
data = kunit_kzalloc(test, sizeof(*data), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, data);
data->card = kunit_kzalloc(test, sizeof(struct fw_card), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, data->card);
data->card->device = kunit_device_register(test, "dummy-device");
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, data->card->device);
test->priv = data;
return 0;
}
static void node_tree_test_exit(struct kunit *test)
{
struct private_data *data = test->priv;
kunit_device_unregister(test, data->card->device);
kunit_kfree(test, data->card);
kunit_kfree(test, data);
}
static void release_fw_node(struct fw_card *card, struct fw_node *node, struct fw_node *parent)
{
struct private_data *data = kunit_get_current_test()->priv;
fw_node_put(node);
++data->release_count;
}
static void node_tree_test_two_nodes(struct kunit *test)
{
// root
// ++============++
// || phy 1 ||
// || P0 P1 P2 ||
// ++===|==|==|==++
// |
// +-----+
// |
// ++===|==x==x==++
// || P0 P1 P2 ||
// || phy 0 ||
// ++============++
//
static const u32 self_id_sequence[] = {
0x80000080,
0x8100005e,
};
struct private_data *data = test->priv;
struct fw_card *card = data->card;
card->node_id = LOCAL_BUS | 0x01;
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
KUNIT_EXPECT_NOT_NULL(test, card->local_node);
KUNIT_EXPECT_PTR_EQ(test, card->local_node, card->root_node);
struct fw_node *node = card->root_node;
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x01);
KUNIT_EXPECT_EQ(test, node->port_count, 3);
KUNIT_EXPECT_NULL(test, node->ports[0]);
KUNIT_EXPECT_NULL(test, node->ports[1]);
KUNIT_EXPECT_NOT_NULL(test, node->ports[2]);
struct fw_node *parent = node;
node = parent->ports[2];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x00);
KUNIT_EXPECT_EQ(test, node->port_count, 1);
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
++card->color;
for_each_fw_node(card, card->root_node, release_fw_node);
KUNIT_EXPECT_EQ(test, data->release_count, 2);
}
static void node_tree_test_two_nodes_1394a(struct kunit *test)
{
// root
// ++===============++
// || phy 0 ||
// || P0 P1 P2 P3 ||
// ++===|==|==|==|==++
// |
// +--+
// |
// ++===|==|==|==|==|==++
// || P0 P1 P2 P3 P4 ||
// || phy 1 ||
// ++==================++
//
// NOTE: Just for Self-ID Packets Zero and One.
static const u32 self_id_sequence[] = {
0x80000065, 0x80814000,
0x8100005d, 0x81810000,
};
struct private_data *data = test->priv;
struct fw_card *card = data->card;
card->node_id = LOCAL_BUS | 0x01;
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
KUNIT_EXPECT_NOT_NULL(test, card->local_node);
KUNIT_EXPECT_PTR_EQ(test, card->local_node, card->root_node);
struct fw_node *node = card->root_node;
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x01);
KUNIT_EXPECT_EQ(test, node->port_count, 4);
KUNIT_EXPECT_NULL(test, node->ports[0]);
KUNIT_EXPECT_NULL(test, node->ports[1]);
KUNIT_EXPECT_NOT_NULL(test, node->ports[2]);
KUNIT_EXPECT_NULL(test, node->ports[3]);
struct fw_node *parent = node;
node = parent->ports[2];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x00);
KUNIT_EXPECT_EQ(test, node->port_count, 5);
KUNIT_EXPECT_NULL(test, node->ports[0]);
KUNIT_EXPECT_PTR_EQ(test, node->ports[1], parent);
KUNIT_EXPECT_NULL(test, node->ports[2]);
KUNIT_EXPECT_NULL(test, node->ports[3]);
KUNIT_EXPECT_NULL(test, node->ports[4]);
++card->color;
for_each_fw_node(card, card->root_node, release_fw_node);
KUNIT_EXPECT_EQ(test, data->release_count, 2);
}
static void node_tree_test_three_nodes_case0(struct kunit *test)
{
// root
// ++============++
// || phy 2 ||
// || P0 P1 P2 ||
// ++===|==|==|==++
// | |
// +--+ +-----------------+
// | |
// ++===|==|==x==++ ++===|==|==|==++
// || P0 P1 P2 || || P0 P1 P2 ||
// || phy 0 || || phy 1 ||
// ++============++ ++============++
//
static const u32 self_id_sequence[] = {
0x80000060,
0x81000058,
0x820000dc,
};
struct private_data *data = test->priv;
struct fw_card *card = data->card;
card->node_id = LOCAL_BUS | 0x02;
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
KUNIT_EXPECT_NOT_NULL(test, card->local_node);
KUNIT_EXPECT_PTR_EQ(test, card->local_node, card->root_node);
struct fw_node *node = card->root_node;
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x02);
KUNIT_EXPECT_EQ(test, node->port_count, 3);
KUNIT_EXPECT_NOT_NULL(test, node->ports[0]);
KUNIT_EXPECT_NULL(test, node->ports[1]);
KUNIT_EXPECT_NOT_NULL(test, node->ports[2]);
struct fw_node *parent = node;
node = parent->ports[0];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x00);
KUNIT_EXPECT_EQ(test, node->port_count, 2);
KUNIT_EXPECT_NULL(test, node->ports[0]);
KUNIT_EXPECT_PTR_EQ(test, node->ports[1], parent);
node = parent->ports[2];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x01);
KUNIT_EXPECT_EQ(test, node->port_count, 3);
KUNIT_EXPECT_NULL(test, node->ports[0]);
KUNIT_EXPECT_NULL(test, node->ports[1]);
KUNIT_EXPECT_PTR_EQ(test, node->ports[2], parent);
++card->color;
for_each_fw_node(card, card->root_node, release_fw_node);
KUNIT_EXPECT_EQ(test, data->release_count, 3);
}
static void node_tree_test_three_nodes_case1(struct kunit *test)
{
// root
// ++============++
// || phy 2 ||
// || P0 P1 P2 ||
// ++===|==|==x==++
// |
// | +-----------+
// | | |
// ++===|==|==|==++ ++===|==x==x==++
// || P0 P1 P2 || || P0 P1 P2 ||
// || phy 1 || || phy 0 ||
// ++============++ ++============++
//
static const u32 self_id_sequence[] = {
0x80000080,
0x8100006c,
0x82000070,
};
struct private_data *data = test->priv;
struct fw_card *card = data->card;
card->node_id = LOCAL_BUS | 0x02;
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
KUNIT_EXPECT_NOT_NULL(test, card->local_node);
KUNIT_EXPECT_PTR_EQ(test, card->local_node, card->root_node);
struct fw_node *node = card->root_node;
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x02);
KUNIT_EXPECT_EQ(test, node->port_count, 2);
KUNIT_EXPECT_NULL(test, node->ports[0]);
KUNIT_EXPECT_NOT_NULL(test, node->ports[1]);
struct fw_node *parent = node;
node = parent->ports[1];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x01);
KUNIT_EXPECT_EQ(test, node->port_count, 3);
KUNIT_EXPECT_NULL(test, node->ports[0]);
KUNIT_EXPECT_PTR_EQ(test, node->ports[1], parent);
KUNIT_EXPECT_NOT_NULL(test, node->ports[2]);
parent = node;
node = parent->ports[2];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x00);
KUNIT_EXPECT_EQ(test, node->port_count, 1);
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
++card->color;
for_each_fw_node(card, card->root_node, release_fw_node);
KUNIT_EXPECT_EQ(test, data->release_count, 3);
}
static void node_tree_test_four_nodes_case0(struct kunit *test)
{
// root
// ++============++
// || phy 3 ||
// || P0 P1 P2 ||
// ++===|==|==|==++
// |
// | +-----------+ +--------------+
// | | | | |
// ++===|==|==|==++ ++===|==|==x==++ ++===|==x==x==++
// || P0 P1 P2 || || P0 P1 P2 || || P0 P1 P2 ||
// || phy 2 || || phy 1 || || phy 0 ||
// ++============++ ++============++ ++============++
//
static const u32 self_id_sequence[] = {
0x80000080,
0x810000b0,
0x8200006c,
0x83000074,
};
struct private_data *data = test->priv;
struct fw_card *card = data->card;
card->node_id = LOCAL_BUS | 0x03;
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
KUNIT_EXPECT_NOT_NULL(test, card->local_node);
KUNIT_EXPECT_PTR_EQ(test, card->local_node, card->root_node);
struct fw_node *node = card->root_node;
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x03);
KUNIT_EXPECT_EQ(test, node->port_count, 3);
KUNIT_EXPECT_NULL(test, node->ports[0]);
KUNIT_EXPECT_NOT_NULL(test, node->ports[1]);
KUNIT_EXPECT_NULL(test, node->ports[2]);
struct fw_node *parent = node;
node = parent->ports[1];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x02);
KUNIT_EXPECT_EQ(test, node->port_count, 3);
KUNIT_EXPECT_NULL(test, node->ports[0]);
KUNIT_EXPECT_PTR_EQ(test, node->ports[1], parent);
KUNIT_EXPECT_NOT_NULL(test, node->ports[2]);
parent = node;
node = parent->ports[2];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x01);
KUNIT_EXPECT_EQ(test, node->port_count, 2);
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
KUNIT_EXPECT_NOT_NULL(test, node->ports[1]);
parent = node;
node = parent->ports[1];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x00);
KUNIT_EXPECT_EQ(test, node->port_count, 1);
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
++card->color;
for_each_fw_node(card, card->root_node, release_fw_node);
KUNIT_EXPECT_EQ(test, data->release_count, 4);
}
static void node_tree_test_four_nodes_case1(struct kunit *test)
{
// root
// ++============++
// || phy 3 ||
// || P0 P1 P2 ||
// ++===|==|==x==++
// |
// | +--------------------------------+
// | | +-----------+ |
// ++===|==|==|==++ ++===|==x==x==++ ++===|==|==|==++
// || P0 P1 P2 || || P0 P1 P2 || || P0 P1 P2 ||
// || phy 2 || || phy 1 || || phy 0 ||
// ++============++ ++============++ ++============++
//
static const u32 self_id_sequence[] = {
0x80000094,
0x81000080,
0x820000bc,
0x830000d0,
};
struct private_data *data = test->priv;
struct fw_card *card = data->card;
card->node_id = LOCAL_BUS | 0x03;
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
KUNIT_EXPECT_NOT_NULL(test, card->local_node);
KUNIT_EXPECT_PTR_EQ(test, card->local_node, card->root_node);
struct fw_node *node = card->root_node;
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x03);
KUNIT_EXPECT_EQ(test, node->port_count, 2);
KUNIT_EXPECT_NOT_NULL(test, node->ports[0]);
KUNIT_EXPECT_NULL(test, node->ports[1]);
struct fw_node *parent = node;
node = parent->ports[0];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x02);
KUNIT_EXPECT_EQ(test, node->port_count, 3);
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
KUNIT_EXPECT_NOT_NULL(test, node->ports[1]);
KUNIT_EXPECT_NOT_NULL(test, node->ports[2]);
parent = node;
node = parent->ports[2];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x01);
KUNIT_EXPECT_EQ(test, node->port_count, 1);
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
node = parent->ports[1];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x00);
KUNIT_EXPECT_EQ(test, node->port_count, 3);
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
KUNIT_EXPECT_NULL(test, node->ports[1]);
KUNIT_EXPECT_NULL(test, node->ports[2]);
++card->color;
for_each_fw_node(card, card->root_node, release_fw_node);
KUNIT_EXPECT_EQ(test, data->release_count, 4);
}
static void node_tree_test_four_nodes_case2(struct kunit *test)
{
// root
// ++============++
// || phy 3 ||
// || P0 P1 P2 ||
// ++===|==|==|==++
// | |
// | +-----------------------------+
// | +--------------+ |
// ++===|==|==x==++ ++===|==|==|==++ ++===|==x==x==++
// || P0 P1 P2 || || P0 P1 P2 || || P0 P1 P2 ||
// || phy 1 || || phy 0 || || phy 2 ||
// ++============++ ++============++ ++============++
//
static const u32 self_id_sequence[] = {
0x80000094,
0x810000b0,
0x82000080,
0x830000dc,
};
struct private_data *data = test->priv;
struct fw_card *card = data->card;
card->node_id = LOCAL_BUS | 0x03;
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
KUNIT_EXPECT_NOT_NULL(test, card->local_node);
KUNIT_EXPECT_PTR_EQ(test, card->local_node, card->root_node);
struct fw_node *node = card->root_node;
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x03);
KUNIT_EXPECT_EQ(test, node->port_count, 3);
KUNIT_EXPECT_NOT_NULL(test, node->ports[0]);
KUNIT_EXPECT_NULL(test, node->ports[1]);
KUNIT_EXPECT_NOT_NULL(test, node->ports[2]);
struct fw_node *parent = node;
node = parent->ports[2];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x02);
KUNIT_EXPECT_EQ(test, node->port_count, 1);
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
node = parent->ports[0];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x01);
KUNIT_EXPECT_EQ(test, node->port_count, 2);
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
KUNIT_EXPECT_NOT_NULL(test, node->ports[1]);
parent = node;
node = parent->ports[1];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x00);
KUNIT_EXPECT_EQ(test, node->port_count, 3);
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
KUNIT_EXPECT_NULL(test, node->ports[1]);
KUNIT_EXPECT_NULL(test, node->ports[2]);
++card->color;
for_each_fw_node(card, card->root_node, release_fw_node);
KUNIT_EXPECT_EQ(test, data->release_count, 4);
}
static void node_tree_test_four_nodes_case3(struct kunit *test)
{
// root
// ++============++
// || phy 3 ||
// || P0 P1 P2 ||
// ++===|==|==|==++
// | | +--------------------------------+
// | +--------------------+ |
// | | |
// ++===|==|==x==++ ++===|==|==|==++ ++===|==|==x==++
// || P0 P1 P2 || || P0 P1 P2 || || P0 P1 P2 ||
// || phy 0 || || phy 1 || || phy 2 ||
// ++============++ ++============++ ++============++
//
static const u32 self_id_sequence[] = {
0x80000090,
0x81000058,
0x82000060,
0x830000fc,
};
struct private_data *data = test->priv;
struct fw_card *card = data->card;
card->node_id = LOCAL_BUS | 0x03;
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
KUNIT_EXPECT_NOT_NULL(test, card->local_node);
KUNIT_EXPECT_PTR_EQ(test, card->local_node, card->root_node);
struct fw_node *node = card->root_node;
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x03);
KUNIT_EXPECT_EQ(test, node->port_count, 3);
KUNIT_EXPECT_NOT_NULL(test, node->ports[0]);
KUNIT_EXPECT_NOT_NULL(test, node->ports[1]);
KUNIT_EXPECT_NOT_NULL(test, node->ports[2]);
struct fw_node *parent = node;
node = parent->ports[2];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x02);
KUNIT_EXPECT_EQ(test, node->port_count, 2);
KUNIT_EXPECT_NULL(test, node->ports[0]);
KUNIT_EXPECT_PTR_EQ(test, node->ports[1], parent);
node = parent->ports[1];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x01);
KUNIT_EXPECT_EQ(test, node->port_count, 3);
KUNIT_EXPECT_NULL(test, node->ports[0]);
KUNIT_EXPECT_NULL(test, node->ports[1]);
KUNIT_EXPECT_PTR_EQ(test, node->ports[2], parent);
node = parent->ports[0];
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x00);
KUNIT_EXPECT_EQ(test, node->port_count, 2);
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
KUNIT_EXPECT_NULL(test, node->ports[1]);
++card->color;
for_each_fw_node(card, card->root_node, release_fw_node);
KUNIT_EXPECT_EQ(test, data->release_count, 4);
}
static void node_tree_test_invalid_extended_self_id_sequence(struct kunit *test)
{
// Use the same node tree as node_tree_test_four_nodes_case1, except for the invalid
// content of self ID packet for the phy 3.
static const u32 self_id_sequence[] = {
0x80000094,
0x81000080,
0x820000bc,
0x830000d1, // Invalid.
};
struct private_data *data = test->priv;
struct fw_card *card = data->card;
card->node_id = LOCAL_BUS | 0x03;
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
KUNIT_EXPECT_NULL(test, card->local_node);
}
static void node_tree_test_invalid_phy_id(struct kunit *test)
{
// Use the same node tree as node_tree_test_four_nodes_case1, except for the invalid
// phy ID for phy 3.
static const u32 self_id_sequence[] = {
0x80000094,
0x81000080,
0x820000bc,
0x8f0000d0, // Invalid.
};
struct private_data *data = test->priv;
struct fw_card *card = data->card;
card->node_id = LOCAL_BUS | 0x03;
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
KUNIT_EXPECT_NULL(test, card->local_node);
}
static void node_tree_test_invalid_child_port_count(struct kunit *test)
{
// Use the same node tree as node_tree_test_four_nodes_case1, except for the invalid
// count of child ports for phy 3.
static const u32 self_id_sequence[] = {
0x80000094,
0x81000080,
0x820000bc,
0x830000fc, // Invalid.
};
struct private_data *data = test->priv;
struct fw_card *card = data->card;
card->node_id = LOCAL_BUS | 0x03;
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
KUNIT_EXPECT_NULL(test, card->local_node);
}
static void node_tree_test_invalid_parent_port_count(struct kunit *test)
{
// Use the same node tree as node_tree_test_four_nodes_case1, except for the invalid
// count of parent ports for phy 3.
static const u32 self_id_sequence[] = {
0x80000094,
0x81000080,
0x820000bc,
0x830000e8, // Invalid.
};
struct private_data *data = test->priv;
struct fw_card *card = data->card;
card->node_id = LOCAL_BUS | 0x03;
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
KUNIT_EXPECT_NULL(test, card->local_node);
}
static struct kunit_case node_tree_test_cases[] = {
KUNIT_CASE(node_tree_test_two_nodes),
KUNIT_CASE(node_tree_test_two_nodes_1394a),
KUNIT_CASE(node_tree_test_three_nodes_case0),
KUNIT_CASE(node_tree_test_three_nodes_case1),
KUNIT_CASE(node_tree_test_four_nodes_case0),
KUNIT_CASE(node_tree_test_four_nodes_case1),
KUNIT_CASE(node_tree_test_four_nodes_case2),
KUNIT_CASE(node_tree_test_four_nodes_case3),
KUNIT_CASE(node_tree_test_invalid_extended_self_id_sequence),
KUNIT_CASE(node_tree_test_invalid_phy_id),
KUNIT_CASE(node_tree_test_invalid_child_port_count),
KUNIT_CASE(node_tree_test_invalid_parent_port_count),
{}
};
static struct kunit_suite node_tree_test_suite = {
.name = "firewire-node-tree",
.init = node_tree_test_init,
.exit = node_tree_test_exit,
.test_cases = node_tree_test_cases,
};
kunit_test_suite(node_tree_test_suite);