1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2025 AIROHA Inc 4 * Author: Lorenzo Bianconi <lorenzo@kernel.org> 5 */ 6 7 #include "airoha_eth.h" 8 9 static void airoha_debugfs_ppe_print_tuple(struct seq_file *m, 10 void *src_addr, void *dest_addr, 11 u16 *src_port, u16 *dest_port, 12 bool ipv6) 13 { 14 __be32 n_addr[IPV6_ADDR_WORDS]; 15 16 if (ipv6) { 17 ipv6_addr_cpu_to_be32(n_addr, src_addr); 18 seq_printf(m, "%pI6", n_addr); 19 } else { 20 seq_printf(m, "%pI4h", src_addr); 21 } 22 if (src_port) 23 seq_printf(m, ":%d", *src_port); 24 25 seq_puts(m, "->"); 26 27 if (ipv6) { 28 ipv6_addr_cpu_to_be32(n_addr, dest_addr); 29 seq_printf(m, "%pI6", n_addr); 30 } else { 31 seq_printf(m, "%pI4h", dest_addr); 32 } 33 if (dest_port) 34 seq_printf(m, ":%d", *dest_port); 35 } 36 37 static int airoha_ppe_debugfs_foe_show(struct seq_file *m, void *private, 38 bool bind) 39 { 40 static const char *const ppe_type_str[] = { 41 [PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T", 42 [PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T", 43 [PPE_PKT_TYPE_BRIDGE] = "L2B", 44 [PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE", 45 [PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T", 46 [PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T", 47 [PPE_PKT_TYPE_IPV6_6RD] = "6RD", 48 }; 49 static const char *const ppe_state_str[] = { 50 [AIROHA_FOE_STATE_INVALID] = "INV", 51 [AIROHA_FOE_STATE_UNBIND] = "UNB", 52 [AIROHA_FOE_STATE_BIND] = "BND", 53 [AIROHA_FOE_STATE_FIN] = "FIN", 54 }; 55 struct airoha_ppe *ppe = m->private; 56 int i; 57 58 for (i = 0; i < PPE_NUM_ENTRIES; i++) { 59 const char *state_str, *type_str = "UNKNOWN"; 60 void *src_addr = NULL, *dest_addr = NULL; 61 u16 *src_port = NULL, *dest_port = NULL; 62 struct airoha_foe_mac_info_common *l2; 63 unsigned char h_source[ETH_ALEN] = {}; 64 struct airoha_foe_stats64 stats = {}; 65 unsigned char h_dest[ETH_ALEN]; 66 struct airoha_foe_entry *hwe; 67 u32 type, state, ib2, data; 68 bool ipv6 = false; 69 70 hwe = airoha_ppe_foe_get_entry(ppe, i); 71 if (!hwe) 72 continue; 73 74 state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, hwe->ib1); 75 if (!state) 76 continue; 77 78 if (bind && state != AIROHA_FOE_STATE_BIND) 79 continue; 80 81 state_str = ppe_state_str[state % ARRAY_SIZE(ppe_state_str)]; 82 type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1); 83 if (type < ARRAY_SIZE(ppe_type_str) && ppe_type_str[type]) 84 type_str = ppe_type_str[type]; 85 86 seq_printf(m, "%05x %s %7s", i, state_str, type_str); 87 88 switch (type) { 89 case PPE_PKT_TYPE_IPV4_HNAPT: 90 case PPE_PKT_TYPE_IPV4_DSLITE: 91 src_port = &hwe->ipv4.orig_tuple.src_port; 92 dest_port = &hwe->ipv4.orig_tuple.dest_port; 93 fallthrough; 94 case PPE_PKT_TYPE_IPV4_ROUTE: 95 src_addr = &hwe->ipv4.orig_tuple.src_ip; 96 dest_addr = &hwe->ipv4.orig_tuple.dest_ip; 97 break; 98 case PPE_PKT_TYPE_IPV6_ROUTE_5T: 99 src_port = &hwe->ipv6.src_port; 100 dest_port = &hwe->ipv6.dest_port; 101 fallthrough; 102 case PPE_PKT_TYPE_IPV6_ROUTE_3T: 103 case PPE_PKT_TYPE_IPV6_6RD: 104 src_addr = &hwe->ipv6.src_ip; 105 dest_addr = &hwe->ipv6.dest_ip; 106 ipv6 = true; 107 break; 108 default: 109 break; 110 } 111 112 if (src_addr && dest_addr) { 113 seq_puts(m, " orig="); 114 airoha_debugfs_ppe_print_tuple(m, src_addr, dest_addr, 115 src_port, dest_port, ipv6); 116 } 117 118 switch (type) { 119 case PPE_PKT_TYPE_IPV4_HNAPT: 120 case PPE_PKT_TYPE_IPV4_DSLITE: 121 src_port = &hwe->ipv4.new_tuple.src_port; 122 dest_port = &hwe->ipv4.new_tuple.dest_port; 123 fallthrough; 124 case PPE_PKT_TYPE_IPV4_ROUTE: 125 src_addr = &hwe->ipv4.new_tuple.src_ip; 126 dest_addr = &hwe->ipv4.new_tuple.dest_ip; 127 seq_puts(m, " new="); 128 airoha_debugfs_ppe_print_tuple(m, src_addr, dest_addr, 129 src_port, dest_port, 130 ipv6); 131 break; 132 default: 133 break; 134 } 135 136 if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { 137 data = hwe->ipv6.data; 138 ib2 = hwe->ipv6.ib2; 139 l2 = &hwe->ipv6.l2; 140 } else { 141 data = hwe->ipv4.data; 142 ib2 = hwe->ipv4.ib2; 143 l2 = &hwe->ipv4.l2.common; 144 *((__be16 *)&h_source[4]) = 145 cpu_to_be16(hwe->ipv4.l2.src_mac_lo); 146 } 147 148 airoha_ppe_foe_entry_get_stats(ppe, i, &stats); 149 150 *((__be32 *)h_dest) = cpu_to_be32(l2->dest_mac_hi); 151 *((__be16 *)&h_dest[4]) = cpu_to_be16(l2->dest_mac_lo); 152 *((__be32 *)h_source) = cpu_to_be32(l2->src_mac_hi); 153 154 seq_printf(m, " eth=%pM->%pM etype=%04x data=%08x" 155 " vlan=%d,%d ib1=%08x ib2=%08x" 156 " packets=%llu bytes=%llu\n", 157 h_source, h_dest, l2->etype, data, 158 l2->vlan1, l2->vlan2, hwe->ib1, ib2, 159 stats.packets, stats.bytes); 160 } 161 162 return 0; 163 } 164 165 static int airoha_ppe_debugfs_foe_all_show(struct seq_file *m, void *private) 166 { 167 return airoha_ppe_debugfs_foe_show(m, private, false); 168 } 169 DEFINE_SHOW_ATTRIBUTE(airoha_ppe_debugfs_foe_all); 170 171 static int airoha_ppe_debugfs_foe_bind_show(struct seq_file *m, void *private) 172 { 173 return airoha_ppe_debugfs_foe_show(m, private, true); 174 } 175 DEFINE_SHOW_ATTRIBUTE(airoha_ppe_debugfs_foe_bind); 176 177 int airoha_ppe_debugfs_init(struct airoha_ppe *ppe) 178 { 179 ppe->debugfs_dir = debugfs_create_dir("ppe", NULL); 180 debugfs_create_file("entries", 0444, ppe->debugfs_dir, ppe, 181 &airoha_ppe_debugfs_foe_all_fops); 182 debugfs_create_file("bind", 0444, ppe->debugfs_dir, ppe, 183 &airoha_ppe_debugfs_foe_bind_fops); 184 185 return 0; 186 } 187