// SPDX-License-Identifier: GPL-2.0-only /* * HYGON CSV interface * * Copyright (C) 2024 Hygon Info Technologies Ltd. * * Author: Liyang Han * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include "psp-dev.h" /* * Hygon CSV build info: * Hygon CSV build info is 32-bit in length other than 8-bit as that * in AMD SEV. */ u32 hygon_csv_build; /* * csv_update_api_version used to update the api version of HYGON CSV * firmwareat driver side. * Currently, we only need to update @hygon_csv_build. */ void csv_update_api_version(struct sev_user_data_status *status) { if (status) { hygon_csv_build = (status->flags >> 9) | ((u32)status->build << 23); } } int csv_cmd_buffer_len(int cmd) { switch (cmd) { case CSV_CMD_HGSC_CERT_IMPORT: return sizeof(struct csv_data_hgsc_cert_import); default: return 0; } } static int csv_ioctl_do_hgsc_import(struct sev_issue_cmd *argp) { struct csv_user_data_hgsc_cert_import input; struct csv_data_hgsc_cert_import *data; void *hgscsk_blob, *hgsc_blob; int ret; if (copy_from_user(&input, (void __user *)argp->data, sizeof(input))) return -EFAULT; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; /* copy HGSCSK certificate blobs from userspace */ hgscsk_blob = psp_copy_user_blob(input.hgscsk_cert_address, input.hgscsk_cert_len); if (IS_ERR(hgscsk_blob)) { ret = PTR_ERR(hgscsk_blob); goto e_free; } data->hgscsk_cert_address = __psp_pa(hgscsk_blob); data->hgscsk_cert_len = input.hgscsk_cert_len; /* copy HGSC certificate blobs from userspace */ hgsc_blob = psp_copy_user_blob(input.hgsc_cert_address, input.hgsc_cert_len); if (IS_ERR(hgsc_blob)) { ret = PTR_ERR(hgsc_blob); goto e_free_hgscsk; } data->hgsc_cert_address = __psp_pa(hgsc_blob); data->hgsc_cert_len = input.hgsc_cert_len; ret = hygon_psp_hooks.__sev_do_cmd_locked(CSV_CMD_HGSC_CERT_IMPORT, data, &argp->error); kfree(hgsc_blob); e_free_hgscsk: kfree(hgscsk_blob); e_free: kfree(data); return ret; } static long csv_ioctl(struct file *file, unsigned int ioctl, unsigned long arg) { void __user *argp = (void __user *)arg; struct sev_issue_cmd input; int ret = -EFAULT; if (!hygon_psp_hooks.sev_dev_hooks_installed) return -ENODEV; if (!psp_master || !psp_master->sev_data) return -ENODEV; if (ioctl != SEV_ISSUE_CMD) return -EINVAL; if (copy_from_user(&input, argp, sizeof(struct sev_issue_cmd))) return -EFAULT; if (input.cmd > CSV_MAX) return -EINVAL; mutex_lock(hygon_psp_hooks.sev_cmd_mutex); switch (input.cmd) { case CSV_HGSC_CERT_IMPORT: ret = csv_ioctl_do_hgsc_import(&input); break; default: /* * If the command is compatible between CSV and SEV, the * native implementation of the driver is invoked. * Release the mutex before calling the native ioctl function * because it will acquires the mutex. */ mutex_unlock(hygon_psp_hooks.sev_cmd_mutex); return hygon_psp_hooks.sev_ioctl(file, ioctl, arg); } if (copy_to_user(argp, &input, sizeof(struct sev_issue_cmd))) ret = -EFAULT; mutex_unlock(hygon_psp_hooks.sev_cmd_mutex); return ret; } const struct file_operations csv_fops = { .owner = THIS_MODULE, .unlocked_ioctl = csv_ioctl, };