]> Git Repo - J-linux.git/blob - drivers/hid/bpf/hid_bpf_dispatch.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / hid / bpf / hid_bpf_dispatch.c
1 // SPDX-License-Identifier: GPL-2.0-only
2
3 /*
4  *  HID-BPF support for Linux
5  *
6  *  Copyright (c) 2022-2024 Benjamin Tissoires
7  */
8
9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10 #include <linux/bitops.h>
11 #include <linux/btf.h>
12 #include <linux/btf_ids.h>
13 #include <linux/filter.h>
14 #include <linux/hid.h>
15 #include <linux/hid_bpf.h>
16 #include <linux/init.h>
17 #include <linux/kfifo.h>
18 #include <linux/minmax.h>
19 #include <linux/module.h>
20 #include "hid_bpf_dispatch.h"
21
22 const struct hid_ops *hid_ops;
23 EXPORT_SYMBOL(hid_ops);
24
25 u8 *
26 dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data,
27                               u32 *size, int interrupt, u64 source, bool from_bpf)
28 {
29         struct hid_bpf_ctx_kern ctx_kern = {
30                 .ctx = {
31                         .hid = hdev,
32                         .allocated_size = hdev->bpf.allocated_data,
33                         .size = *size,
34                 },
35                 .data = hdev->bpf.device_data,
36                 .from_bpf = from_bpf,
37         };
38         struct hid_bpf_ops *e;
39         int ret;
40
41         if (type >= HID_REPORT_TYPES)
42                 return ERR_PTR(-EINVAL);
43
44         /* no program has been attached yet */
45         if (!hdev->bpf.device_data)
46                 return data;
47
48         memset(ctx_kern.data, 0, hdev->bpf.allocated_data);
49         memcpy(ctx_kern.data, data, *size);
50
51         rcu_read_lock();
52         list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) {
53                 if (e->hid_device_event) {
54                         ret = e->hid_device_event(&ctx_kern.ctx, type, source);
55                         if (ret < 0) {
56                                 rcu_read_unlock();
57                                 return ERR_PTR(ret);
58                         }
59
60                         if (ret)
61                                 ctx_kern.ctx.size = ret;
62                 }
63         }
64         rcu_read_unlock();
65
66         ret = ctx_kern.ctx.size;
67         if (ret) {
68                 if (ret > ctx_kern.ctx.allocated_size)
69                         return ERR_PTR(-EINVAL);
70
71                 *size = ret;
72         }
73
74         return ctx_kern.data;
75 }
76 EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event);
77
78 int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
79                                   unsigned char reportnum, u8 *buf,
80                                   u32 size, enum hid_report_type rtype,
81                                   enum hid_class_request reqtype,
82                                   u64 source, bool from_bpf)
83 {
84         struct hid_bpf_ctx_kern ctx_kern = {
85                 .ctx = {
86                         .hid = hdev,
87                         .allocated_size = size,
88                         .size = size,
89                 },
90                 .data = buf,
91                 .from_bpf = from_bpf,
92         };
93         struct hid_bpf_ops *e;
94         int ret, idx;
95
96         if (rtype >= HID_REPORT_TYPES)
97                 return -EINVAL;
98
99         idx = srcu_read_lock(&hdev->bpf.srcu);
100         list_for_each_entry_srcu(e, &hdev->bpf.prog_list, list,
101                                  srcu_read_lock_held(&hdev->bpf.srcu)) {
102                 if (!e->hid_hw_request)
103                         continue;
104
105                 ret = e->hid_hw_request(&ctx_kern.ctx, reportnum, rtype, reqtype, source);
106                 if (ret)
107                         goto out;
108         }
109         ret = 0;
110
111 out:
112         srcu_read_unlock(&hdev->bpf.srcu, idx);
113         return ret;
114 }
115 EXPORT_SYMBOL_GPL(dispatch_hid_bpf_raw_requests);
116
117 int dispatch_hid_bpf_output_report(struct hid_device *hdev,
118                                    __u8 *buf, u32 size, u64 source,
119                                    bool from_bpf)
120 {
121         struct hid_bpf_ctx_kern ctx_kern = {
122                 .ctx = {
123                         .hid = hdev,
124                         .allocated_size = size,
125                         .size = size,
126                 },
127                 .data = buf,
128                 .from_bpf = from_bpf,
129         };
130         struct hid_bpf_ops *e;
131         int ret, idx;
132
133         idx = srcu_read_lock(&hdev->bpf.srcu);
134         list_for_each_entry_srcu(e, &hdev->bpf.prog_list, list,
135                                  srcu_read_lock_held(&hdev->bpf.srcu)) {
136                 if (!e->hid_hw_output_report)
137                         continue;
138
139                 ret = e->hid_hw_output_report(&ctx_kern.ctx, source);
140                 if (ret)
141                         goto out;
142         }
143         ret = 0;
144
145 out:
146         srcu_read_unlock(&hdev->bpf.srcu, idx);
147         return ret;
148 }
149 EXPORT_SYMBOL_GPL(dispatch_hid_bpf_output_report);
150
151 const u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size)
152 {
153         int ret;
154         struct hid_bpf_ctx_kern ctx_kern = {
155                 .ctx = {
156                         .hid = hdev,
157                         .size = *size,
158                         .allocated_size = HID_MAX_DESCRIPTOR_SIZE,
159                 },
160         };
161
162         if (!hdev->bpf.rdesc_ops)
163                 goto ignore_bpf;
164
165         ctx_kern.data = kzalloc(ctx_kern.ctx.allocated_size, GFP_KERNEL);
166         if (!ctx_kern.data)
167                 goto ignore_bpf;
168
169         memcpy(ctx_kern.data, rdesc, min_t(unsigned int, *size, HID_MAX_DESCRIPTOR_SIZE));
170
171         ret = hdev->bpf.rdesc_ops->hid_rdesc_fixup(&ctx_kern.ctx);
172         if (ret < 0)
173                 goto ignore_bpf;
174
175         if (ret) {
176                 if (ret > ctx_kern.ctx.allocated_size)
177                         goto ignore_bpf;
178
179                 *size = ret;
180         }
181
182         return krealloc(ctx_kern.data, *size, GFP_KERNEL);
183
184  ignore_bpf:
185         kfree(ctx_kern.data);
186         return rdesc;
187 }
188 EXPORT_SYMBOL_GPL(call_hid_bpf_rdesc_fixup);
189
190 static int device_match_id(struct device *dev, const void *id)
191 {
192         struct hid_device *hdev = to_hid_device(dev);
193
194         return hdev->id == *(int *)id;
195 }
196
197 struct hid_device *hid_get_device(unsigned int hid_id)
198 {
199         struct device *dev;
200
201         if (!hid_ops)
202                 return ERR_PTR(-EINVAL);
203
204         dev = bus_find_device(hid_ops->bus_type, NULL, &hid_id, device_match_id);
205         if (!dev)
206                 return ERR_PTR(-EINVAL);
207
208         return to_hid_device(dev);
209 }
210
211 void hid_put_device(struct hid_device *hid)
212 {
213         put_device(&hid->dev);
214 }
215
216 static int __hid_bpf_allocate_data(struct hid_device *hdev, u8 **data, u32 *size)
217 {
218         u8 *alloc_data;
219         unsigned int i, j, max_report_len = 0;
220         size_t alloc_size = 0;
221
222         /* compute the maximum report length for this device */
223         for (i = 0; i < HID_REPORT_TYPES; i++) {
224                 struct hid_report_enum *report_enum = hdev->report_enum + i;
225
226                 for (j = 0; j < HID_MAX_IDS; j++) {
227                         struct hid_report *report = report_enum->report_id_hash[j];
228
229                         if (report)
230                                 max_report_len = max(max_report_len, hid_report_len(report));
231                 }
232         }
233
234         /*
235          * Give us a little bit of extra space and some predictability in the
236          * buffer length we create. This way, we can tell users that they can
237          * work on chunks of 64 bytes of memory without having the bpf verifier
238          * scream at them.
239          */
240         alloc_size = DIV_ROUND_UP(max_report_len, 64) * 64;
241
242         alloc_data = kzalloc(alloc_size, GFP_KERNEL);
243         if (!alloc_data)
244                 return -ENOMEM;
245
246         *data = alloc_data;
247         *size = alloc_size;
248
249         return 0;
250 }
251
252 int hid_bpf_allocate_event_data(struct hid_device *hdev)
253 {
254         /* hdev->bpf.device_data is already allocated, abort */
255         if (hdev->bpf.device_data)
256                 return 0;
257
258         return __hid_bpf_allocate_data(hdev, &hdev->bpf.device_data, &hdev->bpf.allocated_data);
259 }
260
261 int hid_bpf_reconnect(struct hid_device *hdev)
262 {
263         if (!test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status)) {
264                 /* trigger call to call_hid_bpf_rdesc_fixup() during the next probe */
265                 hdev->bpf_rsize = 0;
266                 return device_reprobe(&hdev->dev);
267         }
268
269         return 0;
270 }
271
272 /* Disables missing prototype warnings */
273 __bpf_kfunc_start_defs();
274
275 /**
276  * hid_bpf_get_data - Get the kernel memory pointer associated with the context @ctx
277  *
278  * @ctx: The HID-BPF context
279  * @offset: The offset within the memory
280  * @rdwr_buf_size: the const size of the buffer
281  *
282  * @returns %NULL on error, an %__u8 memory pointer on success
283  */
284 __bpf_kfunc __u8 *
285 hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t rdwr_buf_size)
286 {
287         struct hid_bpf_ctx_kern *ctx_kern;
288
289         if (!ctx)
290                 return NULL;
291
292         ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);
293
294         if (rdwr_buf_size + offset > ctx->allocated_size)
295                 return NULL;
296
297         return ctx_kern->data + offset;
298 }
299
300 /**
301  * hid_bpf_allocate_context - Allocate a context to the given HID device
302  *
303  * @hid_id: the system unique identifier of the HID device
304  *
305  * @returns A pointer to &struct hid_bpf_ctx on success, %NULL on error.
306  */
307 __bpf_kfunc struct hid_bpf_ctx *
308 hid_bpf_allocate_context(unsigned int hid_id)
309 {
310         struct hid_device *hdev;
311         struct hid_bpf_ctx_kern *ctx_kern = NULL;
312
313         hdev = hid_get_device(hid_id);
314         if (IS_ERR(hdev))
315                 return NULL;
316
317         ctx_kern = kzalloc(sizeof(*ctx_kern), GFP_KERNEL);
318         if (!ctx_kern) {
319                 hid_put_device(hdev);
320                 return NULL;
321         }
322
323         ctx_kern->ctx.hid = hdev;
324
325         return &ctx_kern->ctx;
326 }
327
328 /**
329  * hid_bpf_release_context - Release the previously allocated context @ctx
330  *
331  * @ctx: the HID-BPF context to release
332  *
333  */
334 __bpf_kfunc void
335 hid_bpf_release_context(struct hid_bpf_ctx *ctx)
336 {
337         struct hid_bpf_ctx_kern *ctx_kern;
338         struct hid_device *hid;
339
340         ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);
341         hid = (struct hid_device *)ctx_kern->ctx.hid; /* ignore const */
342
343         kfree(ctx_kern);
344
345         /* get_device() is called by bus_find_device() */
346         hid_put_device(hid);
347 }
348
349 static int
350 __hid_bpf_hw_check_params(struct hid_bpf_ctx *ctx, __u8 *buf, size_t *buf__sz,
351                           enum hid_report_type rtype)
352 {
353         struct hid_report_enum *report_enum;
354         struct hid_report *report;
355         u32 report_len;
356
357         /* check arguments */
358         if (!ctx || !hid_ops || !buf)
359                 return -EINVAL;
360
361         switch (rtype) {
362         case HID_INPUT_REPORT:
363         case HID_OUTPUT_REPORT:
364         case HID_FEATURE_REPORT:
365                 break;
366         default:
367                 return -EINVAL;
368         }
369
370         if (*buf__sz < 1)
371                 return -EINVAL;
372
373         report_enum = ctx->hid->report_enum + rtype;
374         report = hid_ops->hid_get_report(report_enum, buf);
375         if (!report)
376                 return -EINVAL;
377
378         report_len = hid_report_len(report);
379
380         if (*buf__sz > report_len)
381                 *buf__sz = report_len;
382
383         return 0;
384 }
385
386 /**
387  * hid_bpf_hw_request - Communicate with a HID device
388  *
389  * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context()
390  * @buf: a %PTR_TO_MEM buffer
391  * @buf__sz: the size of the data to transfer
392  * @rtype: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT)
393  * @reqtype: the type of the request (%HID_REQ_GET_REPORT, %HID_REQ_SET_REPORT, ...)
394  *
395  * @returns %0 on success, a negative error code otherwise.
396  */
397 __bpf_kfunc int
398 hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,
399                    enum hid_report_type rtype, enum hid_class_request reqtype)
400 {
401         struct hid_bpf_ctx_kern *ctx_kern;
402         size_t size = buf__sz;
403         u8 *dma_data;
404         int ret;
405
406         ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);
407
408         if (ctx_kern->from_bpf)
409                 return -EDEADLOCK;
410
411         /* check arguments */
412         ret = __hid_bpf_hw_check_params(ctx, buf, &size, rtype);
413         if (ret)
414                 return ret;
415
416         switch (reqtype) {
417         case HID_REQ_GET_REPORT:
418         case HID_REQ_GET_IDLE:
419         case HID_REQ_GET_PROTOCOL:
420         case HID_REQ_SET_REPORT:
421         case HID_REQ_SET_IDLE:
422         case HID_REQ_SET_PROTOCOL:
423                 break;
424         default:
425                 return -EINVAL;
426         }
427
428         dma_data = kmemdup(buf, size, GFP_KERNEL);
429         if (!dma_data)
430                 return -ENOMEM;
431
432         ret = hid_ops->hid_hw_raw_request(ctx->hid,
433                                               dma_data[0],
434                                               dma_data,
435                                               size,
436                                               rtype,
437                                               reqtype,
438                                               (u64)(long)ctx,
439                                               true); /* prevent infinite recursions */
440
441         if (ret > 0)
442                 memcpy(buf, dma_data, ret);
443
444         kfree(dma_data);
445         return ret;
446 }
447
448 /**
449  * hid_bpf_hw_output_report - Send an output report to a HID device
450  *
451  * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context()
452  * @buf: a %PTR_TO_MEM buffer
453  * @buf__sz: the size of the data to transfer
454  *
455  * Returns the number of bytes transferred on success, a negative error code otherwise.
456  */
457 __bpf_kfunc int
458 hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz)
459 {
460         struct hid_bpf_ctx_kern *ctx_kern;
461         size_t size = buf__sz;
462         u8 *dma_data;
463         int ret;
464
465         ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);
466         if (ctx_kern->from_bpf)
467                 return -EDEADLOCK;
468
469         /* check arguments */
470         ret = __hid_bpf_hw_check_params(ctx, buf, &size, HID_OUTPUT_REPORT);
471         if (ret)
472                 return ret;
473
474         dma_data = kmemdup(buf, size, GFP_KERNEL);
475         if (!dma_data)
476                 return -ENOMEM;
477
478         ret = hid_ops->hid_hw_output_report(ctx->hid, dma_data, size, (u64)(long)ctx, true);
479
480         kfree(dma_data);
481         return ret;
482 }
483
484 static int
485 __hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf,
486                        size_t size, bool lock_already_taken)
487 {
488         struct hid_bpf_ctx_kern *ctx_kern;
489         int ret;
490
491         ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);
492         if (ctx_kern->from_bpf)
493                 return -EDEADLOCK;
494
495         /* check arguments */
496         ret = __hid_bpf_hw_check_params(ctx, buf, &size, type);
497         if (ret)
498                 return ret;
499
500         return hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (u64)(long)ctx, true,
501                                          lock_already_taken);
502 }
503
504 /**
505  * hid_bpf_try_input_report - Inject a HID report in the kernel from a HID device
506  *
507  * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context()
508  * @type: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT)
509  * @buf: a %PTR_TO_MEM buffer
510  * @buf__sz: the size of the data to transfer
511  *
512  * Returns %0 on success, a negative error code otherwise. This function will immediately
513  * fail if the device is not available, thus can be safely used in IRQ context.
514  */
515 __bpf_kfunc int
516 hid_bpf_try_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf,
517                          const size_t buf__sz)
518 {
519         struct hid_bpf_ctx_kern *ctx_kern;
520         bool from_hid_event_hook;
521
522         ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);
523         from_hid_event_hook = ctx_kern->data && ctx_kern->data == ctx->hid->bpf.device_data;
524
525         return __hid_bpf_input_report(ctx, type, buf, buf__sz, from_hid_event_hook);
526 }
527
528 /**
529  * hid_bpf_input_report - Inject a HID report in the kernel from a HID device
530  *
531  * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context()
532  * @type: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT)
533  * @buf: a %PTR_TO_MEM buffer
534  * @buf__sz: the size of the data to transfer
535  *
536  * Returns %0 on success, a negative error code otherwise. This function will wait for the
537  * device to be available before injecting the event, thus needs to be called in sleepable
538  * context.
539  */
540 __bpf_kfunc int
541 hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf,
542                      const size_t buf__sz)
543 {
544         int ret;
545
546         ret = down_interruptible(&ctx->hid->driver_input_lock);
547         if (ret)
548                 return ret;
549
550         /* check arguments */
551         ret = __hid_bpf_input_report(ctx, type, buf, buf__sz, true /* lock_already_taken */);
552
553         up(&ctx->hid->driver_input_lock);
554
555         return ret;
556 }
557 __bpf_kfunc_end_defs();
558
559 /*
560  * The following set contains all functions we agree BPF programs
561  * can use.
562  */
563 BTF_KFUNCS_START(hid_bpf_kfunc_ids)
564 BTF_ID_FLAGS(func, hid_bpf_get_data, KF_RET_NULL)
565 BTF_ID_FLAGS(func, hid_bpf_allocate_context, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)
566 BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE | KF_SLEEPABLE)
567 BTF_ID_FLAGS(func, hid_bpf_hw_request, KF_SLEEPABLE)
568 BTF_ID_FLAGS(func, hid_bpf_hw_output_report, KF_SLEEPABLE)
569 BTF_ID_FLAGS(func, hid_bpf_input_report, KF_SLEEPABLE)
570 BTF_ID_FLAGS(func, hid_bpf_try_input_report)
571 BTF_KFUNCS_END(hid_bpf_kfunc_ids)
572
573 static const struct btf_kfunc_id_set hid_bpf_kfunc_set = {
574         .owner = THIS_MODULE,
575         .set   = &hid_bpf_kfunc_ids,
576 };
577
578 /* for syscall HID-BPF */
579 BTF_KFUNCS_START(hid_bpf_syscall_kfunc_ids)
580 BTF_ID_FLAGS(func, hid_bpf_allocate_context, KF_ACQUIRE | KF_RET_NULL)
581 BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE)
582 BTF_ID_FLAGS(func, hid_bpf_hw_request)
583 BTF_ID_FLAGS(func, hid_bpf_hw_output_report)
584 BTF_ID_FLAGS(func, hid_bpf_input_report)
585 BTF_KFUNCS_END(hid_bpf_syscall_kfunc_ids)
586
587 static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = {
588         .owner = THIS_MODULE,
589         .set   = &hid_bpf_syscall_kfunc_ids,
590 };
591
592 int hid_bpf_connect_device(struct hid_device *hdev)
593 {
594         bool need_to_allocate = false;
595         struct hid_bpf_ops *e;
596
597         rcu_read_lock();
598         list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) {
599                 if (e->hid_device_event) {
600                         need_to_allocate = true;
601                         break;
602                 }
603         }
604         rcu_read_unlock();
605
606         /* only allocate BPF data if there are programs attached */
607         if (!need_to_allocate)
608                 return 0;
609
610         return hid_bpf_allocate_event_data(hdev);
611 }
612 EXPORT_SYMBOL_GPL(hid_bpf_connect_device);
613
614 void hid_bpf_disconnect_device(struct hid_device *hdev)
615 {
616         kfree(hdev->bpf.device_data);
617         hdev->bpf.device_data = NULL;
618         hdev->bpf.allocated_data = 0;
619 }
620 EXPORT_SYMBOL_GPL(hid_bpf_disconnect_device);
621
622 void hid_bpf_destroy_device(struct hid_device *hdev)
623 {
624         if (!hdev)
625                 return;
626
627         /* mark the device as destroyed in bpf so we don't reattach it */
628         hdev->bpf.destroyed = true;
629
630         __hid_bpf_ops_destroy_device(hdev);
631
632         synchronize_srcu(&hdev->bpf.srcu);
633         cleanup_srcu_struct(&hdev->bpf.srcu);
634 }
635 EXPORT_SYMBOL_GPL(hid_bpf_destroy_device);
636
637 int hid_bpf_device_init(struct hid_device *hdev)
638 {
639         INIT_LIST_HEAD(&hdev->bpf.prog_list);
640         mutex_init(&hdev->bpf.prog_list_lock);
641         return init_srcu_struct(&hdev->bpf.srcu);
642 }
643 EXPORT_SYMBOL_GPL(hid_bpf_device_init);
644
645 static int __init hid_bpf_init(void)
646 {
647         int err;
648
649         /* Note: if we exit with an error any time here, we would entirely break HID, which
650          * is probably not something we want. So we log an error and return success.
651          *
652          * This is not a big deal: nobody will be able to use the functionality.
653          */
654
655         err = register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &hid_bpf_kfunc_set);
656         if (err) {
657                 pr_warn("error while setting HID BPF tracing kfuncs: %d", err);
658                 return 0;
659         }
660
661         err = register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &hid_bpf_syscall_kfunc_set);
662         if (err) {
663                 pr_warn("error while setting HID BPF syscall kfuncs: %d", err);
664                 return 0;
665         }
666
667         return 0;
668 }
669
670 late_initcall(hid_bpf_init);
671 MODULE_AUTHOR("Benjamin Tissoires");
672 MODULE_LICENSE("GPL");
This page took 0.063493 seconds and 4 git commands to generate.