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 static const __u8 rdesc[] = { 129 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ 130 0x09, 0x32, /* USAGE (Z) */ 131 0x95, 0x01, /* REPORT_COUNT (1) */ 132 0x81, 0x06, /* INPUT (Data,Var,Rel) */ 133 134 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ 135 0x19, 0x01, /* USAGE_MINIMUM (1) */ 136 0x29, 0x03, /* USAGE_MAXIMUM (3) */ 137 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 138 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 139 0x95, 0x03, /* REPORT_COUNT (3) */ 140 0x75, 0x01, /* REPORT_SIZE (1) */ 141 0x91, 0x02, /* Output (Data,Var,Abs) */ 142 0x95, 0x01, /* REPORT_COUNT (1) */ 143 0x75, 0x05, /* REPORT_SIZE (5) */ 144 0x91, 0x01, /* Output (Cnst,Var,Abs) */ 145 146 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ 147 0x19, 0x06, /* USAGE_MINIMUM (6) */ 148 0x29, 0x08, /* USAGE_MAXIMUM (8) */ 149 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 150 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 151 0x95, 0x03, /* REPORT_COUNT (3) */ 152 0x75, 0x01, /* REPORT_SIZE (1) */ 153 0xb1, 0x02, /* Feature (Data,Var,Abs) */ 154 0x95, 0x01, /* REPORT_COUNT (1) */ 155 0x75, 0x05, /* REPORT_SIZE (5) */ 156 0x91, 0x01, /* Output (Cnst,Var,Abs) */ 157 158 0xc0, /* END_COLLECTION */ 159 0xc0, /* END_COLLECTION */ 160 }; 161 162 SEC("?fmod_ret/hid_bpf_rdesc_fixup") 163 int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx) 164 { 165 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4096 /* size */); 166 167 if (!data) 168 return 0; /* EPERM check */ 169 170 callback2_check = data[4]; 171 172 /* insert rdesc at offset 73 */ 173 __builtin_memcpy(&data[73], rdesc, sizeof(rdesc)); 174 175 /* Change Usage Vendor globally */ 176 data[4] = 0x42; 177 178 return sizeof(rdesc) + 73; 179 } 180 181 SEC("?fmod_ret/hid_bpf_device_event") 182 int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx) 183 { 184 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 185 186 if (!data) 187 return 0; /* EPERM check */ 188 189 /* we need to be run first */ 190 if (data[2] || data[3]) 191 return -1; 192 193 data[1] = 1; 194 195 return 0; 196 } 197 198 SEC("?fmod_ret/hid_bpf_device_event") 199 int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx) 200 { 201 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 202 203 if (!data) 204 return 0; /* EPERM check */ 205 206 /* after insert0 and before insert2 */ 207 if (!data[1] || data[3]) 208 return -1; 209 210 data[2] = 2; 211 212 return 0; 213 } 214 215 SEC("?fmod_ret/hid_bpf_device_event") 216 int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx) 217 { 218 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 219 220 if (!data) 221 return 0; /* EPERM check */ 222 223 /* at the end */ 224 if (!data[1] || !data[2]) 225 return -1; 226 227 data[3] = 3; 228 229 return 0; 230 } 231