| // SPDX-License-Identifier: GPL-2.0-only | |
| // | |
| // config-rom-generator-test.c - An application of Kunit to test configuration ROM generator. | |
| // | |
| // Copyright (c) 2026 Takashi Sakamoto | |
| // | |
| // This file can not be built independently since it is intentionally included in core-card.c. | |
| #include <kunit/test.h> | |
| #include <kunit/device.h> | |
| static const __be32 config_rom_bare[] = { | |
| cpu_to_be32(0x0404921b), // bus info | |
| cpu_to_be32(0x31333934), // | | |
| cpu_to_be32(0xf000b223), // | | |
| cpu_to_be32(0x01234567), // | | |
| cpu_to_be32(0x89abcdef), // v | |
| cpu_to_be32(0x00051b70), // root directory | |
| cpu_to_be32(0x0c0083c0), // | | |
| cpu_to_be32(0x03001f11), // | | |
| cpu_to_be32(0x81000003), // | | |
| cpu_to_be32(0x17023901), // | | |
| cpu_to_be32(0x81000008), // v | |
| cpu_to_be32(0x00064cb7), // text descriptor leaf (from root) | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x4c696e75), // | | |
| cpu_to_be32(0x78204669), // | | |
| cpu_to_be32(0x72657769), // | | |
| cpu_to_be32(0x72650000), // v | |
| cpu_to_be32(0x0003ff1c), // text descriptor leaf (from root) | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x4a756a75), // v Juju is a code name at the development time of this stack. | |
| }; | |
| // Following to Configuration ROM for AV/C Devices 1.0 (Dec. 2000. 1394 Trading Association, | |
| // Document 1999027). | |
| #define UNIT_SPEC_ID_1394TA 0x0000a02d | |
| #define UNIT_SW_VERSION_AVC 0x00010001 | |
| #define UNIT_SW_VERSION_IIDC_0104 0x00000100 | |
| static const u32 avc_unit_directory_and_leaf[] = { | |
| 0x00040000, // Unit directory consists of below 4 quads. | |
| (CSR_SPECIFIER_ID << 24) | UNIT_SPEC_ID_1394TA, | |
| (CSR_VERSION << 24) | UNIT_SW_VERSION_AVC, | |
| (CSR_MODEL << 24) | 0x00260827, // Today. | |
| ((CSR_LEAF | CSR_DESCRIPTOR) << 24) | 0x00000001, // Point to next quadlet. | |
| 0x00030000, // Text leaf consists of below 3 quads. | |
| 0x00000000, | |
| 0x00000000, | |
| 0x50756900, // Pui is the name of a cat that the author takes care of. | |
| }; | |
| static const __be32 config_rom_with_avc_unit[] = { | |
| cpu_to_be32(0x0404c1e5), // bus info | |
| cpu_to_be32(0x31333934), // | | |
| cpu_to_be32(0xf000b233), // | | |
| cpu_to_be32(0x01234567), // | | |
| cpu_to_be32(0x89abcdef), // v | |
| cpu_to_be32(0x0006a2d2), // root directory | |
| cpu_to_be32(0x0c0083c0), // | | |
| cpu_to_be32(0x03001f11), // | | |
| cpu_to_be32(0x81000004), // | | |
| cpu_to_be32(0x17023901), // | | |
| cpu_to_be32(0x81000009), // | | |
| cpu_to_be32(0xd100000c), // v | |
| cpu_to_be32(0x00064cb7), // text descriptor leaf (from root) | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x4c696e75), // | | |
| cpu_to_be32(0x78204669), // | | |
| cpu_to_be32(0x72657769), // | | |
| cpu_to_be32(0x72650000), // v | |
| cpu_to_be32(0x0003ff1c), // text descriptor leaf (from root) | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x4a756a75), // v | |
| cpu_to_be32(0x0004227b), // unit directory (from root) | |
| cpu_to_be32(0x1200a02d), // | | |
| cpu_to_be32(0x13010001), // | | |
| cpu_to_be32(0x17260827), // | | |
| cpu_to_be32(0x81000001), // v | |
| cpu_to_be32(0x0003f771), // text descriptor leaf (from unit) | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x50756900), // v | |
| }; | |
| // Following to 1394-based Digital Camera Specification Version 1.04 (Aug. 1996. 1394 Trading | |
| // Association) | |
| #define IIDC_COMMAND_REGS_BASE 0x00 | |
| #define IIDC_VENDOR_NAME_LEAF 0x01 | |
| #define IIDC_MODEL_NAME_LEAF 0x02 | |
| static const u32 iidc_unit_directories_and_leafs[] = { | |
| 0x00030000, | |
| (CSR_SPECIFIER_ID << 24) | UNIT_SPEC_ID_1394TA, | |
| (CSR_VERSION << 24) | UNIT_SW_VERSION_IIDC_0104, | |
| ((CSR_DIRECTORY | CSR_DEPENDENT_INFO) << 24) | 0x00000001, | |
| 0x00030000, | |
| ((CSR_OFFSET | IIDC_COMMAND_REGS_BASE) << 24) | 0x00012345, | |
| ((CSR_LEAF | IIDC_VENDOR_NAME_LEAF) << 24) | 0x00000002, | |
| ((CSR_LEAF | IIDC_MODEL_NAME_LEAF) << 24) | 0x00000008, | |
| 0x00060000, // Text leaf consists of below 6 quads. | |
| 0x00000000, | |
| 0x00000000, | |
| 0x4c696e75, // Use the same name in root directory. | |
| 0x78204669, | |
| 0x72657769, | |
| 0x72650000, | |
| 0x00040000, // Text leaf consists of below 4 quads. | |
| 0x00000000, | |
| 0x00000000, | |
| 0x43686566, // Chef Cat is a nick name when inventing IEEE 1394 itself. | |
| 0x20436174, | |
| }; | |
| static const __be32 config_rom_with_iidc_unit[] = { | |
| cpu_to_be32(0x04046a3e), // bus info | |
| cpu_to_be32(0x31333934), // | | |
| cpu_to_be32(0xf000b243), // | | |
| cpu_to_be32(0x01234567), // | | |
| cpu_to_be32(0x89abcdef), // v | |
| cpu_to_be32(0x0006a2d2), // root directory | |
| cpu_to_be32(0x0c0083c0), // | | |
| cpu_to_be32(0x03001f11), // | | |
| cpu_to_be32(0x81000004), // | | |
| cpu_to_be32(0x17023901), // | | |
| cpu_to_be32(0x81000009), // | | |
| cpu_to_be32(0xd100000c), // v | |
| cpu_to_be32(0x00064cb7), // text descriptor leaf (from root) | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x4c696e75), // | | |
| cpu_to_be32(0x78204669), // | | |
| cpu_to_be32(0x72657769), // | | |
| cpu_to_be32(0x72650000), // v | |
| cpu_to_be32(0x0003ff1c), // text descriptor leaf (from root) | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x4a756a75), // v | |
| cpu_to_be32(0x0003d7fe), // unit directory (from root) | |
| cpu_to_be32(0x1200a02d), // | | |
| cpu_to_be32(0x13000100), // | | |
| cpu_to_be32(0xd4000001), // v | |
| cpu_to_be32(0x0003ea57), // dependent directory (from unit directory) | |
| cpu_to_be32(0x40012345), // | | |
| cpu_to_be32(0x81000002), // | | |
| cpu_to_be32(0x82000008), // v | |
| cpu_to_be32(0x00064cb7), // text descriptor leaf (from dependent directory) | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x4c696e75), // | | |
| cpu_to_be32(0x78204669), // | | |
| cpu_to_be32(0x72657769), // | | |
| cpu_to_be32(0x72650000), // v | |
| cpu_to_be32(0x0004a3e9), // text descriptor leaf (from dependent directory) | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x43686566), // | | |
| cpu_to_be32(0x20436174), // v | |
| }; | |
| static const __be32 config_rom_with_avc_and_iidc_units[] = { | |
| cpu_to_be32(0x040439c0), // bus info | |
| cpu_to_be32(0x31333934), // | | |
| cpu_to_be32(0xf000b253), // | | |
| cpu_to_be32(0x01234567), // | | |
| cpu_to_be32(0x89abcdef), // v | |
| cpu_to_be32(0x0007073e), // root directory | |
| cpu_to_be32(0x0c0083c0), // | | |
| cpu_to_be32(0x03001f11), // | | |
| cpu_to_be32(0x81000005), // | | |
| cpu_to_be32(0x17023901), // | | |
| cpu_to_be32(0x8100000a), // | | |
| cpu_to_be32(0xd100000d), // | | |
| cpu_to_be32(0xd1000015), // v | |
| cpu_to_be32(0x00064cb7), // text descriptor leaf (from root) | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x4c696e75), // | | |
| cpu_to_be32(0x78204669), // | | |
| cpu_to_be32(0x72657769), // | | |
| cpu_to_be32(0x72650000), // v | |
| cpu_to_be32(0x0003ff1c), // text descriptor leaf (from root) | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x4a756a75), // v | |
| cpu_to_be32(0x0004227b), // unit directory (from root) | |
| cpu_to_be32(0x1200a02d), // | | |
| cpu_to_be32(0x13010001), // | | |
| cpu_to_be32(0x17260827), // | | |
| cpu_to_be32(0x81000001), // v | |
| cpu_to_be32(0x0003f771), // text descriptor leaf (from unit) | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x50756900), // v | |
| cpu_to_be32(0x0003d7fe), // unit directory (from root) | |
| cpu_to_be32(0x1200a02d), // | | |
| cpu_to_be32(0x13000100), // | | |
| cpu_to_be32(0xd4000001), // v | |
| cpu_to_be32(0x0003ea57), // dependent directory (from unit directory) | |
| cpu_to_be32(0x40012345), // | | |
| cpu_to_be32(0x81000002), // | | |
| cpu_to_be32(0x82000008), // v | |
| cpu_to_be32(0x00064cb7), // text descriptor leaf (from dependent directory) | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x4c696e75), // | | |
| cpu_to_be32(0x78204669), // | | |
| cpu_to_be32(0x72657769), // | | |
| cpu_to_be32(0x72650000), // v | |
| cpu_to_be32(0x0004a3e9), // text descriptor leaf (from dependent directory) | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x00000000), // | | |
| cpu_to_be32(0x43686566), // | | |
| cpu_to_be32(0x20436174), // v | |
| }; | |
| static const struct generator_test_case { | |
| const char *const name; | |
| int config_rom_generation; | |
| const __be32 *const expected; | |
| unsigned int quadlet_length; | |
| } generator_test_cases[] = { | |
| { | |
| .name = "bare", | |
| .config_rom_generation = 0, | |
| .expected = config_rom_bare, | |
| .quadlet_length = ARRAY_SIZE(config_rom_bare), | |
| }, | |
| { | |
| .name = "with_avc_unit", | |
| .config_rom_generation = 1, | |
| .expected = config_rom_with_avc_unit, | |
| .quadlet_length = ARRAY_SIZE(config_rom_with_avc_unit), | |
| }, | |
| { | |
| .name = "with_iidc_unit", | |
| .config_rom_generation = 2, | |
| .expected = config_rom_with_iidc_unit, | |
| .quadlet_length = ARRAY_SIZE(config_rom_with_iidc_unit), | |
| }, | |
| { | |
| .name = "with_avc_and_iidc_unit", | |
| .config_rom_generation = 3, | |
| .expected = config_rom_with_avc_and_iidc_units, | |
| .quadlet_length = ARRAY_SIZE(config_rom_with_avc_and_iidc_units), | |
| }, | |
| }; | |
| // Define generator_test_gen_params. | |
| KUNIT_ARRAY_PARAM_DESC(generator_test, generator_test_cases, name); | |
| struct state_data { | |
| struct fw_card card; | |
| __be32 config_rom[(CSR_CONFIG_ROM_END - CSR_CONFIG_ROM) / sizeof(__be32)]; | |
| }; | |
| static void test_config_rom_generator(struct kunit *test) | |
| { | |
| // Use kernel stack since they should be mutable for doubly linked-list. | |
| struct fw_descriptor avc_unit_entry = { | |
| .length = ARRAY_SIZE(avc_unit_directory_and_leaf), | |
| .key = (CSR_DIRECTORY | CSR_UNIT) << 24, | |
| .data = avc_unit_directory_and_leaf, | |
| }; | |
| struct fw_descriptor iidc_unit_entry = { | |
| .length = ARRAY_SIZE(iidc_unit_directories_and_leafs), | |
| .key = (CSR_DIRECTORY | CSR_UNIT) << 24, | |
| .data = iidc_unit_directories_and_leafs, | |
| }; | |
| const struct generator_test_case *test_case = test->param_value; | |
| struct state_data *state = test->priv; | |
| struct fw_card *card = &state->card; | |
| __be32 *config_rom = state->config_rom; | |
| card->config_rom_generation = test_case->config_rom_generation; | |
| card->link_speed = SCODE_800; | |
| card->max_receive = 11; | |
| card->guid = 0x0123456789abcdefULL; | |
| if (test_case->expected == config_rom_with_avc_unit || | |
| test_case->expected == config_rom_with_avc_and_iidc_units) | |
| KUNIT_EXPECT_EQ(test, fw_core_add_descriptor(&avc_unit_entry), 0); | |
| if (test_case->expected == config_rom_with_iidc_unit || | |
| test_case->expected == config_rom_with_avc_and_iidc_units) | |
| KUNIT_EXPECT_EQ(test, fw_core_add_descriptor(&iidc_unit_entry), 0); | |
| scoped_guard(mutex, &card_mutex) { | |
| generate_config_rom(card, config_rom); | |
| KUNIT_EXPECT_EQ(test, config_rom_length, test_case->quadlet_length); | |
| } | |
| KUNIT_EXPECT_MEMEQ(test, config_rom, test_case->expected, | |
| sizeof(*test_case->expected) * test_case->quadlet_length); | |
| if (test_case->expected == config_rom_with_iidc_unit || | |
| test_case->expected == config_rom_with_avc_and_iidc_units) | |
| fw_core_remove_descriptor(&iidc_unit_entry); | |
| if (test_case->expected == config_rom_with_avc_unit || | |
| test_case->expected == config_rom_with_avc_and_iidc_units) | |
| fw_core_remove_descriptor(&avc_unit_entry); | |
| } | |
| static void add_descriptor_with_invalid_length(struct kunit *test) | |
| { | |
| // Use kernel stack since they should be mutable for doubly linked-list. | |
| struct fw_descriptor entry_with_invalid_length = { | |
| .length = 257, | |
| }; | |
| KUNIT_EXPECT_EQ(test, fw_core_add_descriptor(&entry_with_invalid_length), -EINVAL); | |
| } | |
| static void add_descriptor_with_invalid_data(struct kunit *test) | |
| { | |
| // Use vendor directory defined in Annex A of Configuration ROM for AV/C Devices 1.0 (Dec. | |
| // 2000, 1394 Trading Association, TA Document 1999027). | |
| static const u32 invalid_vendor_directory[] = { | |
| 0x00020000, | |
| (CSR_MODEL << 24) | 0x00009402, | |
| ((CSR_LEAF | CSR_DESCRIPTOR) << 24) | 0x00000001, | |
| 0xffff0000, // The length should be 6, invalid. | |
| 0x00000000, | |
| 0x00000000, | |
| 0x436f6e63, | |
| 0x6174204e, | |
| 0x6f746174, | |
| 0x696f6e00, | |
| }; | |
| // Use kernel stack since they should be mutable for doubly linked-list. | |
| struct fw_descriptor entry_with_invalid_data = { | |
| .length = ARRAY_SIZE(invalid_vendor_directory), | |
| .key = (CSR_DIRECTORY | CSR_VENDOR) << 24, | |
| .data = invalid_vendor_directory, | |
| }; | |
| KUNIT_EXPECT_EQ(test, fw_core_add_descriptor(&entry_with_invalid_data), -EINVAL); | |
| } | |
| static void add_descriptor_beyond_upper_limit(struct kunit *test) | |
| { | |
| // Use kernel stack since they should be mutable for doubly linked-list. | |
| struct fw_descriptor entry_beyond_upper_limit = { | |
| .length = 255, | |
| .key = (CSR_DIRECTORY | CSR_UNIT) << 24, | |
| .data = NULL, | |
| }; | |
| u32 *data; | |
| data = kunit_kzalloc(test, entry_beyond_upper_limit.length, GFP_KERNEL); | |
| KUNIT_ASSERT_NOT_NULL(test, data); | |
| data[0] = (entry_beyond_upper_limit.length - 1) << 16; | |
| entry_beyond_upper_limit.data = data; | |
| KUNIT_EXPECT_EQ(test, fw_core_add_descriptor(&entry_beyond_upper_limit), -EBUSY); | |
| kunit_kfree(test, data); | |
| } | |
| static const struct fw_card_driver dummy_card_driver; | |
| static int config_rom_generator_test_init(struct kunit *test) | |
| { | |
| struct state_data *state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL); | |
| KUNIT_ASSERT_NOT_NULL(test, state); | |
| struct device *dev = kunit_device_register(test, "dummy-device"); | |
| KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); | |
| fw_card_initialize(&state->card, &dummy_card_driver, dev); | |
| test->priv = state; | |
| return 0; | |
| } | |
| static void config_rom_generator_test_exit(struct kunit *test) | |
| { | |
| struct state_data *state = test->priv; | |
| kunit_device_unregister(test, state->card.device); | |
| kunit_kfree(test, state); | |
| } | |
| static struct kunit_case config_rom_generator_test_cases[] = { | |
| KUNIT_CASE_PARAM(test_config_rom_generator, generator_test_gen_params), | |
| KUNIT_CASE(add_descriptor_with_invalid_length), | |
| KUNIT_CASE(add_descriptor_with_invalid_data), | |
| KUNIT_CASE(add_descriptor_beyond_upper_limit), | |
| {} | |
| }; | |
| static struct kunit_suite config_rom_generator_test_suite = { | |
| .name = "firewire-config-rom-generator", | |
| .init = config_rom_generator_test_init, | |
| .exit = config_rom_generator_test_exit, | |
| .test_cases = config_rom_generator_test_cases, | |
| }; | |
| kunit_test_suite(config_rom_generator_test_suite); |