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
airoha_debugfs_ppe_print_tuple(struct seq_file * m,void * src_addr,void * dest_addr,u16 * src_port,u16 * dest_port,bool ipv6)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
airoha_ppe_debugfs_foe_show(struct seq_file * m,void * private,bool bind)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
airoha_ppe_debugfs_foe_all_show(struct seq_file * m,void * private)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
airoha_ppe_debugfs_foe_bind_show(struct seq_file * m,void * private)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
airoha_ppe_debugfs_init(struct airoha_ppe * ppe)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