firewire updates for v7.3

This update includes error checks, a potential bug fix, and KUnit tests.
 
 Sreeraj S Kurup addressed an error case when generating the contents of
 the configuration ROM with parameters supplied by in-kernel
 implementations such as unit drivers. Some error checks were added to
 handle failures in this path.
 
 Abdun Nihaal identified a potential memory leak when an invalid self-ID
 sequence causes an error while building the internal node tree. KUnit
 tests have been added to trigger this case. This memory leak has existed
 since the first commit and has now been fixed.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQQE66IEYNDXNBPeGKSsLtaWM8LwEwUCaoku5AAKCRCsLtaWM8Lw
 Ew7jAP9Flfyfrf0bfhi4Wmf/L25yNXBS3K5sdbQI5ZlK3Jk9XgEAq0OIsvX1i1Gz
 evFvEjLgu41TTiWOIpPIWoY7OKh0SQA=
 =jJGQ
 -----END PGP SIGNATURE-----

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
commit 47f05f71ad
5 changed files with 687 additions and 30 deletions

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

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

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;

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

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);