From f5102e31c209798cafd2d79463f5093771aadc12 Mon Sep 17 00:00:00 2001 From: Locus Wei-Han Chen Date: Thu, 22 Feb 2024 16:39:46 +0800 Subject: riscv: andes: Support specifying symbolic firmware and hardware raw events Add the Andes AX45 JSON files that allows specifying symbolic event names for the raw PMU events. Signed-off-by: Locus Wei-Han Chen Reviewed-by: Yu Chien Peter Lin Reviewed-by: Charles Ci-Jyun Wu Reviewed-by: Leo Yu-Chi Liang Tested-by: Lad Prabhakar Acked-by: Atish Patra Acked-by: Ian Rogers Link: https://lore.kernel.org/r/20240222083946.3977135-11-peterlin@andestech.com Signed-off-by: Palmer Dabbelt --- .../pmu-events/arch/riscv/andes/ax45/firmware.json | 68 +++++++++++ .../arch/riscv/andes/ax45/instructions.json | 127 +++++++++++++++++++++ .../pmu-events/arch/riscv/andes/ax45/memory.json | 57 +++++++++ .../arch/riscv/andes/ax45/microarch.json | 77 +++++++++++++ tools/perf/pmu-events/arch/riscv/mapfile.csv | 1 + 5 files changed, 330 insertions(+) create mode 100644 tools/perf/pmu-events/arch/riscv/andes/ax45/firmware.json create mode 100644 tools/perf/pmu-events/arch/riscv/andes/ax45/instructions.json create mode 100644 tools/perf/pmu-events/arch/riscv/andes/ax45/memory.json create mode 100644 tools/perf/pmu-events/arch/riscv/andes/ax45/microarch.json (limited to 'tools') diff --git a/tools/perf/pmu-events/arch/riscv/andes/ax45/firmware.json b/tools/perf/pmu-events/arch/riscv/andes/ax45/firmware.json new file mode 100644 index 000000000000..9b4a032186a7 --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/andes/ax45/firmware.json @@ -0,0 +1,68 @@ +[ + { + "ArchStdEvent": "FW_MISALIGNED_LOAD" + }, + { + "ArchStdEvent": "FW_MISALIGNED_STORE" + }, + { + "ArchStdEvent": "FW_ACCESS_LOAD" + }, + { + "ArchStdEvent": "FW_ACCESS_STORE" + }, + { + "ArchStdEvent": "FW_ILLEGAL_INSN" + }, + { + "ArchStdEvent": "FW_SET_TIMER" + }, + { + "ArchStdEvent": "FW_IPI_SENT" + }, + { + "ArchStdEvent": "FW_IPI_RECEIVED" + }, + { + "ArchStdEvent": "FW_FENCE_I_SENT" + }, + { + "ArchStdEvent": "FW_FENCE_I_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_SENT" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_ASID_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_VMID_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_VMID_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_ASID_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_ASID_RECEIVED" + } +] diff --git a/tools/perf/pmu-events/arch/riscv/andes/ax45/instructions.json b/tools/perf/pmu-events/arch/riscv/andes/ax45/instructions.json new file mode 100644 index 000000000000..713a08c1a40f --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/andes/ax45/instructions.json @@ -0,0 +1,127 @@ +[ + { + "EventCode": "0x10", + "EventName": "cycle_count", + "BriefDescription": "Cycle count" + }, + { + "EventCode": "0x20", + "EventName": "inst_count", + "BriefDescription": "Retired instruction count" + }, + { + "EventCode": "0x30", + "EventName": "int_load_inst", + "BriefDescription": "Integer load instruction count" + }, + { + "EventCode": "0x40", + "EventName": "int_store_inst", + "BriefDescription": "Integer store instruction count" + }, + { + "EventCode": "0x50", + "EventName": "atomic_inst", + "BriefDescription": "Atomic instruction count" + }, + { + "EventCode": "0x60", + "EventName": "sys_inst", + "BriefDescription": "System instruction count" + }, + { + "EventCode": "0x70", + "EventName": "int_compute_inst", + "BriefDescription": "Integer computational instruction count" + }, + { + "EventCode": "0x80", + "EventName": "condition_br", + "BriefDescription": "Conditional branch instruction count" + }, + { + "EventCode": "0x90", + "EventName": "taken_condition_br", + "BriefDescription": "Taken conditional branch instruction count" + }, + { + "EventCode": "0xA0", + "EventName": "jal_inst", + "BriefDescription": "JAL instruction count" + }, + { + "EventCode": "0xB0", + "EventName": "jalr_inst", + "BriefDescription": "JALR instruction count" + }, + { + "EventCode": "0xC0", + "EventName": "ret_inst", + "BriefDescription": "Return instruction count" + }, + { + "EventCode": "0xD0", + "EventName": "control_trans_inst", + "BriefDescription": "Control transfer instruction count" + }, + { + "EventCode": "0xE0", + "EventName": "ex9_inst", + "BriefDescription": "EXEC.IT instruction count" + }, + { + "EventCode": "0xF0", + "EventName": "int_mul_inst", + "BriefDescription": "Integer multiplication instruction count" + }, + { + "EventCode": "0x100", + "EventName": "int_div_rem_inst", + "BriefDescription": "Integer division/remainder instruction count" + }, + { + "EventCode": "0x110", + "EventName": "float_load_inst", + "BriefDescription": "Floating-point load instruction count" + }, + { + "EventCode": "0x120", + "EventName": "float_store_inst", + "BriefDescription": "Floating-point store instruction count" + }, + { + "EventCode": "0x130", + "EventName": "float_add_sub_inst", + "BriefDescription": "Floating-point addition/subtraction instruction count" + }, + { + "EventCode": "0x140", + "EventName": "float_mul_inst", + "BriefDescription": "Floating-point multiplication instruction count" + }, + { + "EventCode": "0x150", + "EventName": "float_fused_muladd_inst", + "BriefDescription": "Floating-point fused multiply-add instruction count" + }, + { + "EventCode": "0x160", + "EventName": "float_div_sqrt_inst", + "BriefDescription": "Floating-point division or square-root instruction count" + }, + { + "EventCode": "0x170", + "EventName": "other_float_inst", + "BriefDescription": "Other floating-point instruction count" + }, + { + "EventCode": "0x180", + "EventName": "int_mul_add_sub_inst", + "BriefDescription": "Integer multiplication and add/sub instruction count" + }, + { + "EventCode": "0x190", + "EventName": "retired_ops", + "BriefDescription": "Retired operation count" + } +] diff --git a/tools/perf/pmu-events/arch/riscv/andes/ax45/memory.json b/tools/perf/pmu-events/arch/riscv/andes/ax45/memory.json new file mode 100644 index 000000000000..c7401b526c77 --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/andes/ax45/memory.json @@ -0,0 +1,57 @@ +[ + { + "EventCode": "0x01", + "EventName": "ilm_access", + "BriefDescription": "ILM access" + }, + { + "EventCode": "0x11", + "EventName": "dlm_access", + "BriefDescription": "DLM access" + }, + { + "EventCode": "0x21", + "EventName": "icache_access", + "BriefDescription": "ICACHE access" + }, + { + "EventCode": "0x31", + "EventName": "icache_miss", + "BriefDescription": "ICACHE miss" + }, + { + "EventCode": "0x41", + "EventName": "dcache_access", + "BriefDescription": "DCACHE access" + }, + { + "EventCode": "0x51", + "EventName": "dcache_miss", + "BriefDescription": "DCACHE miss" + }, + { + "EventCode": "0x61", + "EventName": "dcache_load_access", + "BriefDescription": "DCACHE load access" + }, + { + "EventCode": "0x71", + "EventName": "dcache_load_miss", + "BriefDescription": "DCACHE load miss" + }, + { + "EventCode": "0x81", + "EventName": "dcache_store_access", + "BriefDescription": "DCACHE store access" + }, + { + "EventCode": "0x91", + "EventName": "dcache_store_miss", + "BriefDescription": "DCACHE store miss" + }, + { + "EventCode": "0xA1", + "EventName": "dcache_wb", + "BriefDescription": "DCACHE writeback" + } +] diff --git a/tools/perf/pmu-events/arch/riscv/andes/ax45/microarch.json b/tools/perf/pmu-events/arch/riscv/andes/ax45/microarch.json new file mode 100644 index 000000000000..a6d378cbaa74 --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/andes/ax45/microarch.json @@ -0,0 +1,77 @@ +[ + { + "EventCode": "0xB1", + "EventName": "cycle_wait_icache_fill", + "BriefDescription": "Cycles waiting for ICACHE fill data" + }, + { + "EventCode": "0xC1", + "EventName": "cycle_wait_dcache_fill", + "BriefDescription": "Cycles waiting for DCACHE fill data" + }, + { + "EventCode": "0xD1", + "EventName": "uncached_ifetch_from_bus", + "BriefDescription": "Uncached ifetch data access from bus" + }, + { + "EventCode": "0xE1", + "EventName": "uncached_load_from_bus", + "BriefDescription": "Uncached load data access from bus" + }, + { + "EventCode": "0xF1", + "EventName": "cycle_wait_uncached_ifetch", + "BriefDescription": "Cycles waiting for uncached ifetch data from bus" + }, + { + "EventCode": "0x101", + "EventName": "cycle_wait_uncached_load", + "BriefDescription": "Cycles waiting for uncached load data from bus" + }, + { + "EventCode": "0x111", + "EventName": "main_itlb_access", + "BriefDescription": "Main ITLB access" + }, + { + "EventCode": "0x121", + "EventName": "main_itlb_miss", + "BriefDescription": "Main ITLB miss" + }, + { + "EventCode": "0x131", + "EventName": "main_dtlb_access", + "BriefDescription": "Main DTLB access" + }, + { + "EventCode": "0x141", + "EventName": "main_dtlb_miss", + "BriefDescription": "Main DTLB miss" + }, + { + "EventCode": "0x151", + "EventName": "cycle_wait_itlb_fill", + "BriefDescription": "Cycles waiting for Main ITLB fill data" + }, + { + "EventCode": "0x161", + "EventName": "pipe_stall_cycle_dtlb_miss", + "BriefDescription": "Pipeline stall cycles caused by Main DTLB miss" + }, + { + "EventCode": "0x02", + "EventName": "mispredict_condition_br", + "BriefDescription": "Misprediction of conditional branches" + }, + { + "EventCode": "0x12", + "EventName": "mispredict_take_condition_br", + "BriefDescription": "Misprediction of taken conditional branches" + }, + { + "EventCode": "0x22", + "EventName": "mispredict_target_ret_inst", + "BriefDescription": "Misprediction of targets of Return instructions" + } +] diff --git a/tools/perf/pmu-events/arch/riscv/mapfile.csv b/tools/perf/pmu-events/arch/riscv/mapfile.csv index cfc449b19810..3d3a809a5446 100644 --- a/tools/perf/pmu-events/arch/riscv/mapfile.csv +++ b/tools/perf/pmu-events/arch/riscv/mapfile.csv @@ -17,3 +17,4 @@ 0x489-0x8000000000000007-0x[[:xdigit:]]+,v1,sifive/u74,core 0x5b7-0x0-0x0,v1,thead/c900-legacy,core 0x67e-0x80000000db0000[89]0-0x[[:xdigit:]]+,v1,starfive/dubhe-80,core +0x31e-0x8000000000008a45-0x[[:xdigit:]]+,v1,andes/ax45,core -- cgit v1.2.3 From 73d05262a2ca28c72c5aec57d79b47c251fe77c5 Mon Sep 17 00:00:00 2001 From: Charlie Jenkins Date: Tue, 30 Jan 2024 17:07:01 -0800 Subject: selftests: riscv: Generalize mm selftests The behavior of mmap on riscv is defined to not provide an address that uses more bits than the hint address, if provided. Make the tests reflect that. Signed-off-by: Charlie Jenkins Link: https://lore.kernel.org/r/20240130-use_mmap_hint_address-v3-2-8a655cfa8bcb@rivosinc.com Signed-off-by: Palmer Dabbelt --- tools/testing/selftests/riscv/mm/mmap_bottomup.c | 23 +---- tools/testing/selftests/riscv/mm/mmap_default.c | 23 +---- tools/testing/selftests/riscv/mm/mmap_test.h | 107 ++++++++++++++--------- 3 files changed, 67 insertions(+), 86 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/riscv/mm/mmap_bottomup.c b/tools/testing/selftests/riscv/mm/mmap_bottomup.c index 1757d19ca89b..7f7d3eb8b9c9 100644 --- a/tools/testing/selftests/riscv/mm/mmap_bottomup.c +++ b/tools/testing/selftests/riscv/mm/mmap_bottomup.c @@ -6,30 +6,9 @@ TEST(infinite_rlimit) { -// Only works on 64 bit -#if __riscv_xlen == 64 - struct addresses mmap_addresses; - EXPECT_EQ(BOTTOM_UP, memory_layout()); - do_mmaps(&mmap_addresses); - - EXPECT_NE(MAP_FAILED, mmap_addresses.no_hint); - EXPECT_NE(MAP_FAILED, mmap_addresses.on_37_addr); - EXPECT_NE(MAP_FAILED, mmap_addresses.on_38_addr); - EXPECT_NE(MAP_FAILED, mmap_addresses.on_46_addr); - EXPECT_NE(MAP_FAILED, mmap_addresses.on_47_addr); - EXPECT_NE(MAP_FAILED, mmap_addresses.on_55_addr); - EXPECT_NE(MAP_FAILED, mmap_addresses.on_56_addr); - - EXPECT_GT(1UL << 47, (unsigned long)mmap_addresses.no_hint); - EXPECT_GT(1UL << 38, (unsigned long)mmap_addresses.on_37_addr); - EXPECT_GT(1UL << 38, (unsigned long)mmap_addresses.on_38_addr); - EXPECT_GT(1UL << 38, (unsigned long)mmap_addresses.on_46_addr); - EXPECT_GT(1UL << 47, (unsigned long)mmap_addresses.on_47_addr); - EXPECT_GT(1UL << 47, (unsigned long)mmap_addresses.on_55_addr); - EXPECT_GT(1UL << 56, (unsigned long)mmap_addresses.on_56_addr); -#endif + TEST_MMAPS; } TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/riscv/mm/mmap_default.c b/tools/testing/selftests/riscv/mm/mmap_default.c index c63c60b9397e..2ba3ec990006 100644 --- a/tools/testing/selftests/riscv/mm/mmap_default.c +++ b/tools/testing/selftests/riscv/mm/mmap_default.c @@ -6,30 +6,9 @@ TEST(default_rlimit) { -// Only works on 64 bit -#if __riscv_xlen == 64 - struct addresses mmap_addresses; - EXPECT_EQ(TOP_DOWN, memory_layout()); - do_mmaps(&mmap_addresses); - - EXPECT_NE(MAP_FAILED, mmap_addresses.no_hint); - EXPECT_NE(MAP_FAILED, mmap_addresses.on_37_addr); - EXPECT_NE(MAP_FAILED, mmap_addresses.on_38_addr); - EXPECT_NE(MAP_FAILED, mmap_addresses.on_46_addr); - EXPECT_NE(MAP_FAILED, mmap_addresses.on_47_addr); - EXPECT_NE(MAP_FAILED, mmap_addresses.on_55_addr); - EXPECT_NE(MAP_FAILED, mmap_addresses.on_56_addr); - - EXPECT_GT(1UL << 47, (unsigned long)mmap_addresses.no_hint); - EXPECT_GT(1UL << 38, (unsigned long)mmap_addresses.on_37_addr); - EXPECT_GT(1UL << 38, (unsigned long)mmap_addresses.on_38_addr); - EXPECT_GT(1UL << 38, (unsigned long)mmap_addresses.on_46_addr); - EXPECT_GT(1UL << 47, (unsigned long)mmap_addresses.on_47_addr); - EXPECT_GT(1UL << 47, (unsigned long)mmap_addresses.on_55_addr); - EXPECT_GT(1UL << 56, (unsigned long)mmap_addresses.on_56_addr); -#endif + TEST_MMAPS; } TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/riscv/mm/mmap_test.h b/tools/testing/selftests/riscv/mm/mmap_test.h index 9b8434f62f57..36e78d991d5e 100644 --- a/tools/testing/selftests/riscv/mm/mmap_test.h +++ b/tools/testing/selftests/riscv/mm/mmap_test.h @@ -4,60 +4,83 @@ #include #include #include +#include +#include "../../kselftest_harness.h" #define TOP_DOWN 0 #define BOTTOM_UP 1 -struct addresses { - int *no_hint; - int *on_37_addr; - int *on_38_addr; - int *on_46_addr; - int *on_47_addr; - int *on_55_addr; - int *on_56_addr; +#if __riscv_xlen == 64 +uint64_t random_addresses[] = { + 0x19764f0d73b3a9f0, 0x016049584cecef59, 0x3580bdd3562f4acd, + 0x1164219f20b17da0, 0x07d97fcb40ff2373, 0x76ec528921272ee7, + 0x4dd48c38a3de3f70, 0x2e11415055f6997d, 0x14b43334ac476c02, + 0x375a60795aff19f6, 0x47f3051725b8ee1a, 0x4e697cf240494a9f, + 0x456b59b5c2f9e9d1, 0x101724379d63cb96, 0x7fe9ad31619528c1, + 0x2f417247c495c2ea, 0x329a5a5b82943a5e, 0x06d7a9d6adcd3827, + 0x327b0b9ee37f62d5, 0x17c7b1851dfd9b76, 0x006ebb6456ec2cd9, + 0x00836cd14146a134, 0x00e5c4dcde7126db, 0x004c29feadf75753, + 0x00d8b20149ed930c, 0x00d71574c269387a, 0x0006ebe4a82acb7a, + 0x0016135df51f471b, 0x00758bdb55455160, 0x00d0bdd949b13b32, + 0x00ecea01e7c5f54b, 0x00e37b071b9948b1, 0x0011fdd00ff57ab3, + 0x00e407294b52f5ea, 0x00567748c200ed20, 0x000d073084651046, + 0x00ac896f4365463c, 0x00eb0d49a0b26216, 0x0066a2564a982a31, + 0x002e0d20237784ae, 0x0000554ff8a77a76, 0x00006ce07a54c012, + 0x000009570516d799, 0x00000954ca15b84d, 0x0000684f0d453379, + 0x00002ae5816302b5, 0x0000042403fb54bf, 0x00004bad7392bf30, + 0x00003e73bfa4b5e3, 0x00005442c29978e0, 0x00002803f11286b6, + 0x000073875d745fc6, 0x00007cede9cb8240, 0x000027df84cc6a4f, + 0x00006d7e0e74242a, 0x00004afd0b836e02, 0x000047d0e837cd82, + 0x00003b42405efeda, 0x00001531bafa4c95, 0x00007172cae34ac4, }; +#else +uint32_t random_addresses[] = { + 0x8dc302e0, 0x929ab1e0, 0xb47683ba, 0xea519c73, 0xa19f1c90, 0xc49ba213, + 0x8f57c625, 0xadfe5137, 0x874d4d95, 0xaa20f09d, 0xcf21ebfc, 0xda7737f1, + 0xcedf392a, 0x83026c14, 0xccedca52, 0xc6ccf826, 0xe0cd9415, 0x997472ca, + 0xa21a44c1, 0xe82196f5, 0xa23fd66b, 0xc28d5590, 0xd009cdce, 0xcf0be646, + 0x8fc8c7ff, 0xe2a85984, 0xa3d3236b, 0x89a0619d, 0xc03db924, 0xb5d4cc1b, + 0xb96ee04c, 0xd191da48, 0xb432a000, 0xaa2bebbc, 0xa2fcb289, 0xb0cca89b, + 0xb0c18d6a, 0x88f58deb, 0xa4d42d1c, 0xe4d74e86, 0x99902b09, 0x8f786d31, + 0xbec5e381, 0x9a727e65, 0xa9a65040, 0xa880d789, 0x8f1b335e, 0xfc821c1e, + 0x97e34be4, 0xbbef84ed, 0xf447d197, 0xfd7ceee2, 0xe632348d, 0xee4590f4, + 0x958992a5, 0xd57e05d6, 0xfd240970, 0xc5b0dcff, 0xd96da2c2, 0xa7ae041d, +}; +#endif -static inline void do_mmaps(struct addresses *mmap_addresses) -{ - /* - * Place all of the hint addresses on the boundaries of mmap - * sv39, sv48, sv57 - * User addresses end at 1<<38, 1<<47, 1<<56 respectively - */ - void *on_37_bits = (void *)(1UL << 37); - void *on_38_bits = (void *)(1UL << 38); - void *on_46_bits = (void *)(1UL << 46); - void *on_47_bits = (void *)(1UL << 47); - void *on_55_bits = (void *)(1UL << 55); - void *on_56_bits = (void *)(1UL << 56); +#define PROT (PROT_READ | PROT_WRITE) +#define FLAGS (MAP_PRIVATE | MAP_ANONYMOUS) - int prot = PROT_READ | PROT_WRITE; - int flags = MAP_PRIVATE | MAP_ANONYMOUS; +/* mmap must return a value that doesn't use more bits than the hint address. */ +static inline unsigned long get_max_value(unsigned long input) +{ + unsigned long max_bit = (1UL << (((sizeof(unsigned long) * 8) - 1 - + __builtin_clzl(input)))); - mmap_addresses->no_hint = - mmap(NULL, 5 * sizeof(int), prot, flags, 0, 0); - mmap_addresses->on_37_addr = - mmap(on_37_bits, 5 * sizeof(int), prot, flags, 0, 0); - mmap_addresses->on_38_addr = - mmap(on_38_bits, 5 * sizeof(int), prot, flags, 0, 0); - mmap_addresses->on_46_addr = - mmap(on_46_bits, 5 * sizeof(int), prot, flags, 0, 0); - mmap_addresses->on_47_addr = - mmap(on_47_bits, 5 * sizeof(int), prot, flags, 0, 0); - mmap_addresses->on_55_addr = - mmap(on_55_bits, 5 * sizeof(int), prot, flags, 0, 0); - mmap_addresses->on_56_addr = - mmap(on_56_bits, 5 * sizeof(int), prot, flags, 0, 0); + return max_bit + (max_bit - 1); } +#define TEST_MMAPS \ + ({ \ + void *mmap_addr; \ + for (int i = 0; i < ARRAY_SIZE(random_addresses); i++) { \ + mmap_addr = mmap((void *)random_addresses[i], \ + 5 * sizeof(int), PROT, FLAGS, 0, 0); \ + EXPECT_NE(MAP_FAILED, mmap_addr); \ + EXPECT_GE((void *)get_max_value(random_addresses[i]), \ + mmap_addr); \ + mmap_addr = mmap((void *)random_addresses[i], \ + 5 * sizeof(int), PROT, FLAGS, 0, 0); \ + EXPECT_NE(MAP_FAILED, mmap_addr); \ + EXPECT_GE((void *)get_max_value(random_addresses[i]), \ + mmap_addr); \ + } \ + }) + static inline int memory_layout(void) { - int prot = PROT_READ | PROT_WRITE; - int flags = MAP_PRIVATE | MAP_ANONYMOUS; - - void *value1 = mmap(NULL, sizeof(int), prot, flags, 0, 0); - void *value2 = mmap(NULL, sizeof(int), prot, flags, 0, 0); + void *value1 = mmap(NULL, sizeof(int), PROT, FLAGS, 0, 0); + void *value2 = mmap(NULL, sizeof(int), PROT, FLAGS, 0, 0); return value2 > value1; } -- cgit v1.2.3