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("?fmod_ret/hid_bpf_device_event") 18 int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx) 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("?fmod_ret/hid_bpf_device_event") 33 int BPF_PROG(hid_second_event, struct hid_bpf_ctx *hid_ctx) 34 { 35 __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 36 37 if (!rw_data) 38 return 0; /* EPERM check */ 39 40 rw_data[3] = rw_data[2] + 5; 41 42 return hid_ctx->size; 43 } 44 45 SEC("?fmod_ret/hid_bpf_device_event") 46 int BPF_PROG(hid_change_report_id, struct hid_bpf_ctx *hid_ctx) 47 { 48 __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */); 49 50 if (!rw_data) 51 return 0; /* EPERM check */ 52 53 rw_data[0] = 2; 54 55 return 9; 56 } 57 58 SEC("syscall") 59 int attach_prog(struct attach_prog_args *ctx) 60 { 61 ctx->retval = hid_bpf_attach_prog(ctx->hid, 62 ctx->prog_fd, 63 ctx->insert_head ? HID_BPF_FLAG_INSERT_HEAD : 64 HID_BPF_FLAG_NONE); 65 return 0; 66 } 67 68 struct hid_hw_request_syscall_args { 69 /* data needs to come at offset 0 so we can use it in calls */ 70 __u8 data[10]; 71 unsigned int hid; 72 int retval; 73 size_t size; 74 enum hid_report_type type; 75 __u8 request_type; 76 }; 77 78 SEC("syscall") 79 int hid_user_raw_request(struct hid_hw_request_syscall_args *args) 80 { 81 struct hid_bpf_ctx *ctx; 82 const size_t size = args->size; 83 int i, ret = 0; 84 85 if (size > sizeof(args->data)) 86 return -7; /* -E2BIG */ 87 88 ctx = hid_bpf_allocate_context(args->hid); 89 if (!ctx) 90 return -1; /* EPERM check */ 91 92 ret = hid_bpf_hw_request(ctx, 93 args->data, 94 size, 95 args->type, 96 args->request_type); 97 args->retval = ret; 98 99 hid_bpf_release_context(ctx); 100 101 return 0; 102 } 103 104 SEC("syscall") 105 int hid_user_output_report(struct hid_hw_request_syscall_args *args) 106 { 107 struct hid_bpf_ctx *ctx; 108 const size_t size = args->size; 109 int i, ret = 0; 110 111 if (size > sizeof(args->data)) 112 return -7; /* -E2BIG */ 113 114 ctx = hid_bpf_allocate_context(args->hid); 115 if (!ctx) 116 return -1; /* EPERM check */ 117 118 ret = hid_bpf_hw_output_report(ctx, 119 args->data, 120 size); 121 args->retval = ret; 122 123 hid_bpf_release_context(ctx); 124 125 return 0; 126 } 127 128 SEC("syscall") 129 int hid_user_input_report(struct hid_hw_request_syscall_args *args) 130 { 131 struct hid_bpf_ctx *ctx; 132 const size_t size = args->size; 133 int i, ret = 0; 134 135 if (size > sizeof(args->data)) 136 return -7; /* -E2BIG */ 137 138 ctx = hid_bpf_allocate_context(args->hid); 139 if (!ctx) 140 return -1; /* EPERM check */ 141 142 ret = hid_bpf_input_report(ctx, HID_INPUT_REPORT, args->data, size); 143 args->retval = ret; 144 145 hid_bpf_release_context(ctx); 146 147 return 0; 148 } 149 150 static const __u8 rdesc[] = { 151 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ 152 0x09, 0x32, /* USAGE (Z) */ 153 0x95, 0x01, /* REPORT_COUNT (1) */ 154 0x81, 0x06, /* INPUT (Data,Var,Rel) */ 155 156 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ 157 0x19, 0x01, /* USAGE_MINIMUM (1) */ 158 0x29, 0x03, /* USAGE_MAXIMUM (3) */ 159 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 160 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 161 0x95, 0x03, /* REPORT_COUNT (3) */ 162 0x75, 0x01, /* REPORT_SIZE (1) */ 163 0x91, 0x02, /* Output (Data,Var,Abs) */ 164 0x95, 0x01, /* REPORT_COUNT (1) */ 165 0x75, 0x05, /* REPORT_SIZE (5) */ 166 0x91, 0x01, /* Output (Cnst,Var,Abs) */ 167 168 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ 169 0x19, 0x06, /* USAGE_MINIMUM (6) */ 170 0x29, 0x08, /* USAGE_MAXIMUM (8) */ 171 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 172 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 173 0x95, 0x03, /* REPORT_COUNT (3) */ 174 0x75, 0x01, /* REPORT_SIZE (1) */ 175 0xb1, 0x02, /* Feature (Data,Var,Abs) */ 176 0x95, 0x01, /* REPORT_COUNT (1) */ 177 0x75, 0x05, /* REPORT_SIZE (5) */ 178 0x91, 0x01, /* Output (Cnst,Var,Abs) */ 179 180 0xc0, /* END_COLLECTION */ 181 0xc0, /* END_COLLECTION */ 182 }; 183 184 SEC("?fmod_ret/hid_bpf_rdesc_fixup") 185 int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx) 186 { 187 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4096 /* size */); 188 189 if (!data) 190 return 0; /* EPERM check */ 191 192 callback2_check = data[4]; 193 194 /* insert rdesc at offset 73 */ 195 __builtin_memcpy(&data[73], rdesc, sizeof(rdesc)); 196 197 /* Change Usage Vendor globally */ 198 data[4] = 0x42; 199 200 return sizeof(rdesc) + 73; 201 } 202 203 SEC("?fmod_ret/hid_bpf_device_event") 204 int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx) 205 { 206 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 207 208 if (!data) 209 return 0; /* EPERM check */ 210 211 /* we need to be run first */ 212 if (data[2] || data[3]) 213 return -1; 214 215 data[1] = 1; 216 217 return 0; 218 } 219 220 SEC("?fmod_ret/hid_bpf_device_event") 221 int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx) 222 { 223 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 224 225 if (!data) 226 return 0; /* EPERM check */ 227 228 /* after insert0 and before insert2 */ 229 if (!data[1] || data[3]) 230 return -1; 231 232 data[2] = 2; 233 234 return 0; 235 } 236 237 SEC("?fmod_ret/hid_bpf_device_event") 238 int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx) 239 { 240 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 241 242 if (!data) 243 return 0; /* EPERM check */ 244 245 /* at the end */ 246 if (!data[1] || !data[2]) 247 return -1; 248 249 data[3] = 3; 250 251 return 0; 252 } 253