1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2022 Red hat */ 3 #include "hid_bpf_helpers.h" 4 5 char _license[] SEC("license") = "GPL"; 6 7 struct attach_prog_args { 8 int prog_fd; 9 unsigned int hid; 10 int retval; 11 int insert_head; 12 }; 13 14 __u64 callback_check = 52; 15 __u64 callback2_check = 52; 16 17 SEC("?struct_ops/hid_device_event") 18 int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 19 { 20 __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */); 21 22 if (!rw_data) 23 return 0; /* EPERM check */ 24 25 callback_check = rw_data[1]; 26 27 rw_data[2] = rw_data[1] + 5; 28 29 return hid_ctx->size; 30 } 31 32 SEC(".struct_ops.link") 33 struct hid_bpf_ops first_event = { 34 .hid_device_event = (void *)hid_first_event, 35 .hid_id = 2, 36 }; 37 38 SEC("?struct_ops/hid_device_event") 39 int BPF_PROG(hid_second_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 40 { 41 __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 42 43 if (!rw_data) 44 return 0; /* EPERM check */ 45 46 rw_data[3] = rw_data[2] + 5; 47 48 return hid_ctx->size; 49 } 50 51 SEC(".struct_ops.link") 52 struct hid_bpf_ops second_event = { 53 .hid_device_event = (void *)hid_second_event, 54 }; 55 56 SEC("?struct_ops/hid_device_event") 57 int BPF_PROG(hid_change_report_id, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 58 { 59 __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */); 60 61 if (!rw_data) 62 return 0; /* EPERM check */ 63 64 rw_data[0] = 2; 65 66 return 9; 67 } 68 69 SEC(".struct_ops.link") 70 struct hid_bpf_ops change_report_id = { 71 .hid_device_event = (void *)hid_change_report_id, 72 }; 73 74 struct hid_hw_request_syscall_args { 75 /* data needs to come at offset 0 so we can use it in calls */ 76 __u8 data[10]; 77 unsigned int hid; 78 int retval; 79 size_t size; 80 enum hid_report_type type; 81 __u8 request_type; 82 }; 83 84 SEC("syscall") 85 int hid_user_raw_request(struct hid_hw_request_syscall_args *args) 86 { 87 struct hid_bpf_ctx *ctx; 88 const size_t size = args->size; 89 int i, ret = 0; 90 91 if (size > sizeof(args->data)) 92 return -7; /* -E2BIG */ 93 94 ctx = hid_bpf_allocate_context(args->hid); 95 if (!ctx) 96 return -1; /* EPERM check */ 97 98 ret = hid_bpf_hw_request(ctx, 99 args->data, 100 size, 101 args->type, 102 args->request_type); 103 args->retval = ret; 104 105 hid_bpf_release_context(ctx); 106 107 return 0; 108 } 109 110 SEC("syscall") 111 int hid_user_output_report(struct hid_hw_request_syscall_args *args) 112 { 113 struct hid_bpf_ctx *ctx; 114 const size_t size = args->size; 115 int i, ret = 0; 116 117 if (size > sizeof(args->data)) 118 return -7; /* -E2BIG */ 119 120 ctx = hid_bpf_allocate_context(args->hid); 121 if (!ctx) 122 return -1; /* EPERM check */ 123 124 ret = hid_bpf_hw_output_report(ctx, 125 args->data, 126 size); 127 args->retval = ret; 128 129 hid_bpf_release_context(ctx); 130 131 return 0; 132 } 133 134 SEC("syscall") 135 int hid_user_input_report(struct hid_hw_request_syscall_args *args) 136 { 137 struct hid_bpf_ctx *ctx; 138 const size_t size = args->size; 139 int i, ret = 0; 140 141 if (size > sizeof(args->data)) 142 return -7; /* -E2BIG */ 143 144 ctx = hid_bpf_allocate_context(args->hid); 145 if (!ctx) 146 return -1; /* EPERM check */ 147 148 ret = hid_bpf_input_report(ctx, HID_INPUT_REPORT, args->data, size); 149 args->retval = ret; 150 151 hid_bpf_release_context(ctx); 152 153 return 0; 154 } 155 156 static const __u8 rdesc[] = { 157 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ 158 0x09, 0x32, /* USAGE (Z) */ 159 0x95, 0x01, /* REPORT_COUNT (1) */ 160 0x81, 0x06, /* INPUT (Data,Var,Rel) */ 161 162 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ 163 0x19, 0x01, /* USAGE_MINIMUM (1) */ 164 0x29, 0x03, /* USAGE_MAXIMUM (3) */ 165 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 166 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 167 0x95, 0x03, /* REPORT_COUNT (3) */ 168 0x75, 0x01, /* REPORT_SIZE (1) */ 169 0x91, 0x02, /* Output (Data,Var,Abs) */ 170 0x95, 0x01, /* REPORT_COUNT (1) */ 171 0x75, 0x05, /* REPORT_SIZE (5) */ 172 0x91, 0x01, /* Output (Cnst,Var,Abs) */ 173 174 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ 175 0x19, 0x06, /* USAGE_MINIMUM (6) */ 176 0x29, 0x08, /* USAGE_MAXIMUM (8) */ 177 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 178 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 179 0x95, 0x03, /* REPORT_COUNT (3) */ 180 0x75, 0x01, /* REPORT_SIZE (1) */ 181 0xb1, 0x02, /* Feature (Data,Var,Abs) */ 182 0x95, 0x01, /* REPORT_COUNT (1) */ 183 0x75, 0x05, /* REPORT_SIZE (5) */ 184 0x91, 0x01, /* Output (Cnst,Var,Abs) */ 185 186 0xc0, /* END_COLLECTION */ 187 0xc0, /* END_COLLECTION */ 188 }; 189 190 /* 191 * the following program is marked as sleepable (struct_ops.s). 192 * This is not strictly mandatory but is a nice test for 193 * sleepable struct_ops 194 */ 195 SEC("?struct_ops.s/hid_rdesc_fixup") 196 int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx) 197 { 198 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4096 /* size */); 199 200 if (!data) 201 return 0; /* EPERM check */ 202 203 callback2_check = data[4]; 204 205 /* insert rdesc at offset 73 */ 206 __builtin_memcpy(&data[73], rdesc, sizeof(rdesc)); 207 208 /* Change Usage Vendor globally */ 209 data[4] = 0x42; 210 211 return sizeof(rdesc) + 73; 212 } 213 214 SEC(".struct_ops.link") 215 struct hid_bpf_ops rdesc_fixup = { 216 .hid_rdesc_fixup = (void *)hid_rdesc_fixup, 217 }; 218 219 SEC("?struct_ops/hid_device_event") 220 int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 221 { 222 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 223 224 if (!data) 225 return 0; /* EPERM check */ 226 227 /* we need to be run first */ 228 if (data[2] || data[3]) 229 return -1; 230 231 data[1] = 1; 232 233 return 0; 234 } 235 236 SEC(".struct_ops.link") 237 struct hid_bpf_ops test_insert1 = { 238 .hid_device_event = (void *)hid_test_insert1, 239 .flags = BPF_F_BEFORE, 240 }; 241 242 SEC("?struct_ops/hid_device_event") 243 int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 244 { 245 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 246 247 if (!data) 248 return 0; /* EPERM check */ 249 250 /* after insert0 and before insert2 */ 251 if (!data[1] || data[3]) 252 return -1; 253 254 data[2] = 2; 255 256 return 0; 257 } 258 259 SEC(".struct_ops.link") 260 struct hid_bpf_ops test_insert2 = { 261 .hid_device_event = (void *)hid_test_insert2, 262 }; 263 264 SEC("?struct_ops/hid_device_event") 265 int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 266 { 267 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 268 269 if (!data) 270 return 0; /* EPERM check */ 271 272 /* at the end */ 273 if (!data[1] || !data[2]) 274 return -1; 275 276 data[3] = 3; 277 278 return 0; 279 } 280 281 SEC(".struct_ops.link") 282 struct hid_bpf_ops test_insert3 = { 283 .hid_device_event = (void *)hid_test_insert3, 284 }; 285