| /* |
| * |
| * (C) COPYRIGHT 2017 ARM Limited. All rights reserved. |
| * |
| * This program is free software and is provided to you under the terms of the |
| * GNU General Public License version 2 as published by the Free Software |
| * Foundation, and any use by you of this program is subject to the terms |
| * of such GNU licence. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, you can access it online at |
| * http://www.gnu.org/licenses/gpl-2.0.html. |
| * |
| * SPDX-License-Identifier: GPL-2.0 |
| * |
| */ |
| |
| /* Kernel UTF test helpers */ |
| #include <kutf/kutf_helpers.h> |
| |
| #include <linux/err.h> |
| #include <linux/jiffies.h> |
| #include <linux/sched.h> |
| #include <linux/preempt.h> |
| #include <linux/wait.h> |
| #include <linux/uaccess.h> |
| |
| static DEFINE_SPINLOCK(kutf_input_lock); |
| |
| static bool pending_input(struct kutf_context *context) |
| { |
| bool input_pending; |
| |
| spin_lock(&kutf_input_lock); |
| |
| input_pending = !list_empty(&context->userdata.input_head); |
| |
| spin_unlock(&kutf_input_lock); |
| |
| return input_pending; |
| } |
| |
| char *kutf_helper_input_dequeue(struct kutf_context *context, size_t *str_size) |
| { |
| struct kutf_userdata_line *line; |
| |
| spin_lock(&kutf_input_lock); |
| |
| while (list_empty(&context->userdata.input_head)) { |
| int err; |
| |
| kutf_set_waiting_for_input(context->result_set); |
| |
| spin_unlock(&kutf_input_lock); |
| |
| err = wait_event_interruptible(context->userdata.input_waitq, |
| pending_input(context)); |
| |
| if (err) |
| return ERR_PTR(-EINTR); |
| |
| spin_lock(&kutf_input_lock); |
| } |
| |
| line = list_first_entry(&context->userdata.input_head, |
| struct kutf_userdata_line, node); |
| if (line->str) { |
| /* |
| * Unless it is the end-of-input marker, |
| * remove it from the list |
| */ |
| list_del(&line->node); |
| } |
| |
| spin_unlock(&kutf_input_lock); |
| |
| if (str_size) |
| *str_size = line->size; |
| return line->str; |
| } |
| |
| int kutf_helper_input_enqueue(struct kutf_context *context, |
| const char __user *str, size_t size) |
| { |
| struct kutf_userdata_line *line; |
| |
| line = kutf_mempool_alloc(&context->fixture_pool, |
| sizeof(*line) + size + 1); |
| if (!line) |
| return -ENOMEM; |
| if (str) { |
| unsigned long bytes_not_copied; |
| |
| line->size = size; |
| line->str = (void *)(line + 1); |
| bytes_not_copied = copy_from_user(line->str, str, size); |
| if (bytes_not_copied != 0) |
| return -EFAULT; |
| /* Zero terminate the string */ |
| line->str[size] = '\0'; |
| } else { |
| /* This is used to mark the end of input */ |
| WARN_ON(size); |
| line->size = 0; |
| line->str = NULL; |
| } |
| |
| spin_lock(&kutf_input_lock); |
| |
| list_add_tail(&line->node, &context->userdata.input_head); |
| |
| kutf_clear_waiting_for_input(context->result_set); |
| |
| spin_unlock(&kutf_input_lock); |
| |
| wake_up(&context->userdata.input_waitq); |
| |
| return 0; |
| } |
| |
| void kutf_helper_input_enqueue_end_of_data(struct kutf_context *context) |
| { |
| kutf_helper_input_enqueue(context, NULL, 0); |
| } |