/* * * (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 #include #include #include #include #include #include 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); }