Received: by 2002:a05:6a10:2726:0:0:0:0 with SMTP id ib38csp3058096pxb; Mon, 4 Apr 2022 06:53:28 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwa7DY3DJruc1ffdfdyBwE8+uygPAv809s91cYtT6OhE1JXBx35LAxtuUJSMWqyaG7Ix9xk X-Received: by 2002:a17:906:c14b:b0:6da:b30d:76a0 with SMTP id dp11-20020a170906c14b00b006dab30d76a0mr103817ejc.279.1649080407690; Mon, 04 Apr 2022 06:53:27 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1649080407; cv=none; d=google.com; s=arc-20160816; b=J2vbmr4ALW35I4uCXFFOJGgA3F3ok1tMOtZgTP/MDv5ooe2rz6cnX5zd1QMegiXgxx lCrXxvDkocSa8931Z+ecVsKMKap66z9n3auLzrtmwLvJ7uEfSBTgpjpvouebYRH3K6pK aXoFlhjNRFtpVJxlIX2jkirlcIQNorPmGjNboOAIVcEgUW5lMQrJxY3qGjgHQ7g/d9ri XExgEjo2qz8OH1Il2iQ4/qCbc0ipuGKkjafxgJI0nE7ZI927h4s6ezd9Md+4ugq9dcXw dgk2qO/v4YURjxabZlBPJnIQnpzStgPhcVkr0+M9lthBO3T7vhbzb9lYzWWV958v9wKj tcHw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-language:content-transfer-encoding :in-reply-to:mime-version:user-agent:date:message-id:from:references :cc:to:subject:dkim-signature; bh=2s725oGnHNNOcgWpbeNsbPL1/4esJMufPhCaFrrcR1Y=; b=hOA4akPfLLtEHUfEVjuOW88TI4RyWvgzv0HIpN74oIoDBwwP70HvSNLIj3U5XatBG6 PBKmh3tXfDp2F4dtihQzREyJJZi7dzZ3BW6CdUemksJbHwXJwvEE6IZjftW6wDOyjmBp zLXlgvt8AdY22ifAE2y9YkVau90L4o79e6CKLKkJQ6zOWPJzx08F3U6SB3lkbsPKbuuv 1Yg6qTrd7HS6BWPvjH0Y2nxRz/hwQ2Dyi6z81buI6Hw5a/8Ph0189/UBz5tHbnjufvKE ps4+b5E/ad9DmgacwZlL8wpgGtvOi3p4IUMxQap1Xiy+tU6XTK8kG0fIuuI249oR93Dr 9HlA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=K9OJStP1; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id gf14-20020a170906e20e00b006df76385e16si2918165ejb.694.2022.04.04.06.53.00; Mon, 04 Apr 2022 06:53:27 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=K9OJStP1; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1357195AbiDBPSh (ORCPT + 99 others); Sat, 2 Apr 2022 11:18:37 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51358 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1357196AbiDBPRw (ORCPT ); Sat, 2 Apr 2022 11:17:52 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 26835FABEB for ; Sat, 2 Apr 2022 08:15:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1648912557; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=2s725oGnHNNOcgWpbeNsbPL1/4esJMufPhCaFrrcR1Y=; b=K9OJStP1ZCAX9PW/V9m1GcZqYi5FTxe1rqX8bN+XNjTHIMKdgaFx8osalgZ0VcP3SBXw2L //OER6kcxWBJsIzUyYFhLJu6UDCPvozmPlq/DOeTvkiUo7yM1sszwWiqeKlWATmpzJnSsx WRGrd3d8SscAzXCsuSWrMG5FUlRcNh0= Received: from mail-qv1-f70.google.com (mail-qv1-f70.google.com [209.85.219.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-36-RKFcvuLGOAOpq4FAongEQw-1; Sat, 02 Apr 2022 11:15:56 -0400 X-MC-Unique: RKFcvuLGOAOpq4FAongEQw-1 Received: by mail-qv1-f70.google.com with SMTP id cl19-20020a05621404f300b0044103143211so4217263qvb.10 for ; Sat, 02 Apr 2022 08:15:56 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:subject:to:cc:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-transfer-encoding :content-language; bh=2s725oGnHNNOcgWpbeNsbPL1/4esJMufPhCaFrrcR1Y=; b=SAxkwPF67FpQfJ9hA3fOCqTqcYnKsmBvtMnKHDBTUFc9c1COdWSxKIyo+VFfyCDq3L eE/gQ5PCmc7iFsGhKQo5u8Ua28kJJDgEORu8L0ZUU40MSUBKRrhatyWww38lWRxiOqJr ynWg/aE66FJYPj2HNOOqAos3k3sac3CBCWNAMHo0TSIUSvktOhAKYrAisCIT6P234lhd HAz/4ktClVCsv7qhmHRpfEmH6hNjpjFABXE7JLBQfWY4Oy3gmmTLGWleZp7zJj9hmsxS BCgE5Dhv8Jtm1s+2US5pgpyW1OcSczHt5a65tDczgDMGjahCzo1aCjoiqatokd00cXAt P6hg== X-Gm-Message-State: AOAM533X5UchOI6LXeDFZ6ef6EWciTDg/UtDavB4viGYT2P3ScFwhl9T pAAJGfvuO/v3f/+6qRcC9WiYU1FISX0VqXcHO48S8I0Gvmo26pAQt5wlGD3pjENAs3PCL9U4e2A PbbPbcopGfbX0ZAcYtijpHwwI X-Received: by 2002:a05:622a:1711:b0:2e1:eb55:9f9c with SMTP id h17-20020a05622a171100b002e1eb559f9cmr11989456qtk.299.1648912554610; Sat, 02 Apr 2022 08:15:54 -0700 (PDT) X-Received: by 2002:a05:622a:1711:b0:2e1:eb55:9f9c with SMTP id h17-20020a05622a171100b002e1eb559f9cmr11989436qtk.299.1648912554192; Sat, 02 Apr 2022 08:15:54 -0700 (PDT) Received: from localhost.localdomain (024-205-208-113.res.spectrum.com. [24.205.208.113]) by smtp.gmail.com with ESMTPSA id 64-20020a370343000000b0067b31f32693sm3114073qkd.109.2022.04.02.08.15.52 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Sat, 02 Apr 2022 08:15:53 -0700 (PDT) Subject: Re: [RESEND PATCH v1 3/8] firmware_loader: Split sysfs support from fallback To: Russ Weight , mcgrof@kernel.org, gregkh@linuxfoundation.org, rafael@kernel.org, linux-kernel@vger.kernel.org Cc: lgoncalv@redhat.com, yilun.xu@intel.com, hao.wu@intel.com, matthew.gerlach@intel.com, basheer.ahmed.muddebihal@intel.com, tianfei.zhang@intel.com References: <20220323233331.155121-1-russell.h.weight@intel.com> <20220323233331.155121-4-russell.h.weight@intel.com> From: Tom Rix Message-ID: <346561f8-df45-c3aa-a6b9-1328abe80e8f@redhat.com> Date: Sat, 2 Apr 2022 08:15:50 -0700 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.10.1 MIME-Version: 1.0 In-Reply-To: <20220323233331.155121-4-russell.h.weight@intel.com> Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Content-Language: en-US X-Spam-Status: No, score=-2.8 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,NICE_REPLY_A, RCVD_IN_DNSWL_LOW,RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE, SPF_NONE,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On 3/23/22 4:33 PM, Russ Weight wrote: > In preparation for sharing the "loading" and "data" sysfs nodes with the > new firmware upload support, split out sysfs functionality from fallback.c > and fallback.h into sysfs.c and sysfs.h. This includes the firmware > class driver code that is associated with the sysfs files and the > fw_fallback_config support for the timeout sysfs node. > > CONFIG_FW_LOADER_SYSFS is created and is selected by > CONFIG_FW_LOADER_USER_HELPER in order to include sysfs.o in > firmware_class-objs. > > This is mostly just a code reorganization. There are a few symbols that > change in scope, and these can be identified by looking at the header > file changes. A few white-space warnings from checkpatch are also > addressed in this patch. > > Signed-off-by: Russ Weight > --- > v1: > - Renamed files fw_sysfs.c and fw_sysfs.h to sysfs.c and sysfs.h > - Moved "MODULE_IMPORT_NS(FIRMWARE_LOADER_PRIVATE);" from sysfs.c to > sysfs.h to address an error identified by the kernel test robot > > --- > drivers/base/firmware_loader/Kconfig | 4 + > drivers/base/firmware_loader/Makefile | 1 + > drivers/base/firmware_loader/fallback.c | 430 ------------------------ > drivers/base/firmware_loader/fallback.h | 46 +-- > drivers/base/firmware_loader/sysfs.c | 411 ++++++++++++++++++++++ > drivers/base/firmware_loader/sysfs.h | 96 ++++++ > 6 files changed, 513 insertions(+), 475 deletions(-) > create mode 100644 drivers/base/firmware_loader/sysfs.c > create mode 100644 drivers/base/firmware_loader/sysfs.h > > diff --git a/drivers/base/firmware_loader/Kconfig b/drivers/base/firmware_loader/Kconfig > index 38f3b66bf52b..9e03178eee00 100644 > --- a/drivers/base/firmware_loader/Kconfig > +++ b/drivers/base/firmware_loader/Kconfig > @@ -29,6 +29,9 @@ if FW_LOADER > config FW_LOADER_PAGED_BUF > bool > > +config FW_LOADER_SYSFS > + bool > + > config EXTRA_FIRMWARE > string "Build named firmware blobs into the kernel binary" > help > @@ -72,6 +75,7 @@ config EXTRA_FIRMWARE_DIR > > config FW_LOADER_USER_HELPER > bool "Enable the firmware sysfs fallback mechanism" > + select FW_LOADER_SYSFS Is this code reordering necessary ? This config is not removed or renamed later and has the same configs are the later FW_UPLOAD. Maybe leave fallback.c as-is and rename FW_LOADER_USER_HELPER to FW_LOADER_SYSFS because the name is more descriptive. The 'sorry we suck' help message replaced with a shorter message to indicate this is now a more capable config. The later FW_UPLOAD would have a 'depends on FW_LOADER_SYSFS' If you end up needing to do the reorder, move it to patch 1 because bisecting-wise it should not depend on improvements in the current 1,2 patches. Tom > select FW_LOADER_PAGED_BUF > help > This option enables a sysfs loading facility to enable firmware > diff --git a/drivers/base/firmware_loader/Makefile b/drivers/base/firmware_loader/Makefile > index e87843408fe6..aab213f82288 100644 > --- a/drivers/base/firmware_loader/Makefile > +++ b/drivers/base/firmware_loader/Makefile > @@ -6,5 +6,6 @@ obj-$(CONFIG_FW_LOADER) += firmware_class.o > firmware_class-objs := main.o > firmware_class-$(CONFIG_FW_LOADER_USER_HELPER) += fallback.o > firmware_class-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += fallback_platform.o > +firmware_class-$(CONFIG_FW_LOADER_SYSFS) += sysfs.o > > obj-y += builtin/ > diff --git a/drivers/base/firmware_loader/fallback.c b/drivers/base/firmware_loader/fallback.c > index d82e055a4297..bf68e3947814 100644 > --- a/drivers/base/firmware_loader/fallback.c > +++ b/drivers/base/firmware_loader/fallback.c > @@ -3,12 +3,9 @@ > #include > #include > #include > -#include > #include > -#include > #include > #include > -#include > #include > > #include "fallback.h" > @@ -18,22 +15,6 @@ > * firmware fallback mechanism > */ > > -MODULE_IMPORT_NS(FIRMWARE_LOADER_PRIVATE); > - > -extern struct firmware_fallback_config fw_fallback_config; > - > -/* These getters are vetted to use int properly */ > -static inline int __firmware_loading_timeout(void) > -{ > - return fw_fallback_config.loading_timeout; > -} > - > -/* These setters are vetted to use int properly */ > -static void __fw_fallback_set_timeout(int timeout) > -{ > - fw_fallback_config.loading_timeout = timeout; > -} > - > /* > * use small loading timeout for caching devices' firmware because all these > * firmware images have been loaded successfully at lease once, also system is > @@ -58,52 +39,11 @@ static long firmware_loading_timeout(void) > __firmware_loading_timeout() * HZ : MAX_JIFFY_OFFSET; > } > > -static inline bool fw_sysfs_done(struct fw_priv *fw_priv) > -{ > - return __fw_state_check(fw_priv, FW_STATUS_DONE); > -} > - > -static inline bool fw_sysfs_loading(struct fw_priv *fw_priv) > -{ > - return __fw_state_check(fw_priv, FW_STATUS_LOADING); > -} > - > static inline int fw_sysfs_wait_timeout(struct fw_priv *fw_priv, long timeout) > { > return __fw_state_wait_common(fw_priv, timeout); > } > > -struct fw_sysfs { > - bool nowait; > - struct device dev; > - struct fw_priv *fw_priv; > - struct firmware *fw; > -}; > - > -static struct fw_sysfs *to_fw_sysfs(struct device *dev) > -{ > - return container_of(dev, struct fw_sysfs, dev); > -} > - > -static void __fw_load_abort(struct fw_priv *fw_priv) > -{ > - /* > - * There is a small window in which user can write to 'loading' > - * between loading done/aborted and disappearance of 'loading' > - */ > - if (fw_state_is_aborted(fw_priv) || fw_sysfs_done(fw_priv)) > - return; > - > - fw_state_aborted(fw_priv); > -} > - > -static void fw_load_abort(struct fw_sysfs *fw_sysfs) > -{ > - struct fw_priv *fw_priv = fw_sysfs->fw_priv; > - > - __fw_load_abort(fw_priv); > -} > - > static LIST_HEAD(pending_fw_head); > > void kill_pending_fw_fallback_reqs(bool only_kill_custom) > @@ -120,376 +60,6 @@ void kill_pending_fw_fallback_reqs(bool only_kill_custom) > mutex_unlock(&fw_lock); > } > > -static ssize_t timeout_show(struct class *class, struct class_attribute *attr, > - char *buf) > -{ > - return sysfs_emit(buf, "%d\n", __firmware_loading_timeout()); > -} > - > -/** > - * timeout_store() - set number of seconds to wait for firmware > - * @class: device class pointer > - * @attr: device attribute pointer > - * @buf: buffer to scan for timeout value > - * @count: number of bytes in @buf > - * > - * Sets the number of seconds to wait for the firmware. Once > - * this expires an error will be returned to the driver and no > - * firmware will be provided. > - * > - * Note: zero means 'wait forever'. > - **/ > -static ssize_t timeout_store(struct class *class, struct class_attribute *attr, > - const char *buf, size_t count) > -{ > - int tmp_loading_timeout = simple_strtol(buf, NULL, 10); > - > - if (tmp_loading_timeout < 0) > - tmp_loading_timeout = 0; > - > - __fw_fallback_set_timeout(tmp_loading_timeout); > - > - return count; > -} > -static CLASS_ATTR_RW(timeout); > - > -static struct attribute *firmware_class_attrs[] = { > - &class_attr_timeout.attr, > - NULL, > -}; > -ATTRIBUTE_GROUPS(firmware_class); > - > -static void fw_dev_release(struct device *dev) > -{ > - struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev); > - > - kfree(fw_sysfs); > -} > - > -static int do_firmware_uevent(struct fw_sysfs *fw_sysfs, struct kobj_uevent_env *env) > -{ > - if (add_uevent_var(env, "FIRMWARE=%s", fw_sysfs->fw_priv->fw_name)) > - return -ENOMEM; > - if (add_uevent_var(env, "TIMEOUT=%i", __firmware_loading_timeout())) > - return -ENOMEM; > - if (add_uevent_var(env, "ASYNC=%d", fw_sysfs->nowait)) > - return -ENOMEM; > - > - return 0; > -} > - > -static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env) > -{ > - struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev); > - int err = 0; > - > - mutex_lock(&fw_lock); > - if (fw_sysfs->fw_priv) > - err = do_firmware_uevent(fw_sysfs, env); > - mutex_unlock(&fw_lock); > - return err; > -} > - > -static struct class firmware_class = { > - .name = "firmware", > - .class_groups = firmware_class_groups, > - .dev_uevent = firmware_uevent, > - .dev_release = fw_dev_release, > -}; > - > -int register_sysfs_loader(void) > -{ > - int ret = class_register(&firmware_class); > - > - if (ret != 0) > - return ret; > - return register_firmware_config_sysctl(); > -} > - > -void unregister_sysfs_loader(void) > -{ > - unregister_firmware_config_sysctl(); > - class_unregister(&firmware_class); > -} > - > -static ssize_t firmware_loading_show(struct device *dev, > - struct device_attribute *attr, char *buf) > -{ > - struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev); > - int loading = 0; > - > - mutex_lock(&fw_lock); > - if (fw_sysfs->fw_priv) > - loading = fw_sysfs_loading(fw_sysfs->fw_priv); > - mutex_unlock(&fw_lock); > - > - return sysfs_emit(buf, "%d\n", loading); > -} > - > -/** > - * firmware_loading_store() - set value in the 'loading' control file > - * @dev: device pointer > - * @attr: device attribute pointer > - * @buf: buffer to scan for loading control value > - * @count: number of bytes in @buf > - * > - * The relevant values are: > - * > - * 1: Start a load, discarding any previous partial load. > - * 0: Conclude the load and hand the data to the driver code. > - * -1: Conclude the load with an error and discard any written data. > - **/ > -static ssize_t firmware_loading_store(struct device *dev, > - struct device_attribute *attr, > - const char *buf, size_t count) > -{ > - struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev); > - struct fw_priv *fw_priv; > - ssize_t written = count; > - int loading = simple_strtol(buf, NULL, 10); > - > - mutex_lock(&fw_lock); > - fw_priv = fw_sysfs->fw_priv; > - if (fw_state_is_aborted(fw_priv) || fw_state_is_done(fw_priv)) > - goto out; > - > - switch (loading) { > - case 1: > - /* discarding any previous partial load */ > - if (!fw_sysfs_done(fw_priv)) { > - fw_free_paged_buf(fw_priv); > - fw_state_start(fw_priv); > - } > - break; > - case 0: > - if (fw_sysfs_loading(fw_priv)) { > - int rc; > - > - /* > - * Several loading requests may be pending on > - * one same firmware buf, so let all requests > - * see the mapped 'buf->data' once the loading > - * is completed. > - * */ > - rc = fw_map_paged_buf(fw_priv); > - if (rc) > - dev_err(dev, "%s: map pages failed\n", > - __func__); > - else > - rc = security_kernel_post_load_data(fw_priv->data, > - fw_priv->size, > - LOADING_FIRMWARE, "blob"); > - > - /* > - * Same logic as fw_load_abort, only the DONE bit > - * is ignored and we set ABORT only on failure. > - */ > - if (rc) { > - fw_state_aborted(fw_priv); > - written = rc; > - } else { > - fw_state_done(fw_priv); > - } > - break; > - } > - fallthrough; > - default: > - dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading); > - fallthrough; > - case -1: > - fw_load_abort(fw_sysfs); > - break; > - } > -out: > - mutex_unlock(&fw_lock); > - return written; > -} > - > -static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store); > - > -static void firmware_rw_data(struct fw_priv *fw_priv, char *buffer, > - loff_t offset, size_t count, bool read) > -{ > - if (read) > - memcpy(buffer, fw_priv->data + offset, count); > - else > - memcpy(fw_priv->data + offset, buffer, count); > -} > - > -static void firmware_rw(struct fw_priv *fw_priv, char *buffer, > - loff_t offset, size_t count, bool read) > -{ > - while (count) { > - void *page_data; > - int page_nr = offset >> PAGE_SHIFT; > - int page_ofs = offset & (PAGE_SIZE-1); > - int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count); > - > - page_data = kmap(fw_priv->pages[page_nr]); > - > - if (read) > - memcpy(buffer, page_data + page_ofs, page_cnt); > - else > - memcpy(page_data + page_ofs, buffer, page_cnt); > - > - kunmap(fw_priv->pages[page_nr]); > - buffer += page_cnt; > - offset += page_cnt; > - count -= page_cnt; > - } > -} > - > -static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj, > - struct bin_attribute *bin_attr, > - char *buffer, loff_t offset, size_t count) > -{ > - struct device *dev = kobj_to_dev(kobj); > - struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev); > - struct fw_priv *fw_priv; > - ssize_t ret_count; > - > - mutex_lock(&fw_lock); > - fw_priv = fw_sysfs->fw_priv; > - if (!fw_priv || fw_sysfs_done(fw_priv)) { > - ret_count = -ENODEV; > - goto out; > - } > - if (offset > fw_priv->size) { > - ret_count = 0; > - goto out; > - } > - if (count > fw_priv->size - offset) > - count = fw_priv->size - offset; > - > - ret_count = count; > - > - if (fw_priv->data) > - firmware_rw_data(fw_priv, buffer, offset, count, true); > - else > - firmware_rw(fw_priv, buffer, offset, count, true); > - > -out: > - mutex_unlock(&fw_lock); > - return ret_count; > -} > - > -static int fw_realloc_pages(struct fw_sysfs *fw_sysfs, int min_size) > -{ > - int err; > - > - err = fw_grow_paged_buf(fw_sysfs->fw_priv, > - PAGE_ALIGN(min_size) >> PAGE_SHIFT); > - if (err) > - fw_load_abort(fw_sysfs); > - return err; > -} > - > -/** > - * firmware_data_write() - write method for firmware > - * @filp: open sysfs file > - * @kobj: kobject for the device > - * @bin_attr: bin_attr structure > - * @buffer: buffer being written > - * @offset: buffer offset for write in total data store area > - * @count: buffer size > - * > - * Data written to the 'data' attribute will be later handed to > - * the driver as a firmware image. > - **/ > -static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj, > - struct bin_attribute *bin_attr, > - char *buffer, loff_t offset, size_t count) > -{ > - struct device *dev = kobj_to_dev(kobj); > - struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev); > - struct fw_priv *fw_priv; > - ssize_t retval; > - > - if (!capable(CAP_SYS_RAWIO)) > - return -EPERM; > - > - mutex_lock(&fw_lock); > - fw_priv = fw_sysfs->fw_priv; > - if (!fw_priv || fw_sysfs_done(fw_priv)) { > - retval = -ENODEV; > - goto out; > - } > - > - if (fw_priv->data) { > - if (offset + count > fw_priv->allocated_size) { > - retval = -ENOMEM; > - goto out; > - } > - firmware_rw_data(fw_priv, buffer, offset, count, false); > - retval = count; > - } else { > - retval = fw_realloc_pages(fw_sysfs, offset + count); > - if (retval) > - goto out; > - > - retval = count; > - firmware_rw(fw_priv, buffer, offset, count, false); > - } > - > - fw_priv->size = max_t(size_t, offset + count, fw_priv->size); > -out: > - mutex_unlock(&fw_lock); > - return retval; > -} > - > -static struct bin_attribute firmware_attr_data = { > - .attr = { .name = "data", .mode = 0644 }, > - .size = 0, > - .read = firmware_data_read, > - .write = firmware_data_write, > -}; > - > -static struct attribute *fw_dev_attrs[] = { > - &dev_attr_loading.attr, > - NULL > -}; > - > -static struct bin_attribute *fw_dev_bin_attrs[] = { > - &firmware_attr_data, > - NULL > -}; > - > -static const struct attribute_group fw_dev_attr_group = { > - .attrs = fw_dev_attrs, > - .bin_attrs = fw_dev_bin_attrs, > -}; > - > -static const struct attribute_group *fw_dev_attr_groups[] = { > - &fw_dev_attr_group, > - NULL > -}; > - > -static struct fw_sysfs * > -fw_create_instance(struct firmware *firmware, const char *fw_name, > - struct device *device, u32 opt_flags) > -{ > - struct fw_sysfs *fw_sysfs; > - struct device *f_dev; > - > - fw_sysfs = kzalloc(sizeof(*fw_sysfs), GFP_KERNEL); > - if (!fw_sysfs) { > - fw_sysfs = ERR_PTR(-ENOMEM); > - goto exit; > - } > - > - fw_sysfs->nowait = !!(opt_flags & FW_OPT_NOWAIT); > - fw_sysfs->fw = firmware; > - f_dev = &fw_sysfs->dev; > - > - device_initialize(f_dev); > - dev_set_name(f_dev, "%s", fw_name); > - f_dev->parent = device; > - f_dev->class = &firmware_class; > - f_dev->groups = fw_dev_attr_groups; > -exit: > - return fw_sysfs; > -} > - > /** > * fw_load_sysfs_fallback() - load a firmware via the sysfs fallback mechanism > * @fw_sysfs: firmware sysfs information for the firmware to load > diff --git a/drivers/base/firmware_loader/fallback.h b/drivers/base/firmware_loader/fallback.h > index 9f3055d3b4ca..144148595660 100644 > --- a/drivers/base/firmware_loader/fallback.h > +++ b/drivers/base/firmware_loader/fallback.h > @@ -6,29 +6,7 @@ > #include > > #include "firmware.h" > - > -/** > - * struct firmware_fallback_config - firmware fallback configuration settings > - * > - * Helps describe and fine tune the fallback mechanism. > - * > - * @force_sysfs_fallback: force the sysfs fallback mechanism to be used > - * as if one had enabled CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y. > - * Useful to help debug a CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y > - * functionality on a kernel where that config entry has been disabled. > - * @ignore_sysfs_fallback: force to disable the sysfs fallback mechanism. > - * This emulates the behaviour as if we had set the kernel > - * config CONFIG_FW_LOADER_USER_HELPER=n. > - * @old_timeout: for internal use > - * @loading_timeout: the timeout to wait for the fallback mechanism before > - * giving up, in seconds. > - */ > -struct firmware_fallback_config { > - unsigned int force_sysfs_fallback; > - unsigned int ignore_sysfs_fallback; > - int old_timeout; > - int loading_timeout; > -}; > +#include "sysfs.h" > > #ifdef CONFIG_FW_LOADER_USER_HELPER > int firmware_fallback_sysfs(struct firmware *fw, const char *name, > @@ -40,19 +18,6 @@ void kill_pending_fw_fallback_reqs(bool only_kill_custom); > void fw_fallback_set_cache_timeout(void); > void fw_fallback_set_default_timeout(void); > > -int register_sysfs_loader(void); > -void unregister_sysfs_loader(void); > -#ifdef CONFIG_SYSCTL > -extern int register_firmware_config_sysctl(void); > -extern void unregister_firmware_config_sysctl(void); > -#else > -static inline int register_firmware_config_sysctl(void) > -{ > - return 0; > -} > -static inline void unregister_firmware_config_sysctl(void) { } > -#endif /* CONFIG_SYSCTL */ > - > #else /* CONFIG_FW_LOADER_USER_HELPER */ > static inline int firmware_fallback_sysfs(struct firmware *fw, const char *name, > struct device *device, > @@ -66,15 +31,6 @@ static inline int firmware_fallback_sysfs(struct firmware *fw, const char *name, > static inline void kill_pending_fw_fallback_reqs(bool only_kill_custom) { } > static inline void fw_fallback_set_cache_timeout(void) { } > static inline void fw_fallback_set_default_timeout(void) { } > - > -static inline int register_sysfs_loader(void) > -{ > - return 0; > -} > - > -static inline void unregister_sysfs_loader(void) > -{ > -} > #endif /* CONFIG_FW_LOADER_USER_HELPER */ > > #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE > diff --git a/drivers/base/firmware_loader/sysfs.c b/drivers/base/firmware_loader/sysfs.c > new file mode 100644 > index 000000000000..49aeff45b123 > --- /dev/null > +++ b/drivers/base/firmware_loader/sysfs.c > @@ -0,0 +1,411 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +#include > +#include > +#include > +#include > +#include > + > +#include "firmware.h" > +#include "sysfs.h" > + > +/* > + * sysfs support for firmware loader > + */ > + > +static inline bool fw_sysfs_done(struct fw_priv *fw_priv) > +{ > + return __fw_state_check(fw_priv, FW_STATUS_DONE); > +} > + > +static inline bool fw_sysfs_loading(struct fw_priv *fw_priv) > +{ > + return __fw_state_check(fw_priv, FW_STATUS_LOADING); > +} > + > +void __fw_load_abort(struct fw_priv *fw_priv) > +{ > + /* > + * There is a small window in which user can write to 'loading' > + * between loading done/aborted and disappearance of 'loading' > + */ > + if (fw_state_is_aborted(fw_priv) || fw_sysfs_done(fw_priv)) > + return; > + > + fw_state_aborted(fw_priv); > +} > + > +static ssize_t timeout_show(struct class *class, struct class_attribute *attr, > + char *buf) > +{ > + return sysfs_emit(buf, "%d\n", __firmware_loading_timeout()); > +} > + > +/** > + * timeout_store() - set number of seconds to wait for firmware > + * @class: device class pointer > + * @attr: device attribute pointer > + * @buf: buffer to scan for timeout value > + * @count: number of bytes in @buf > + * > + * Sets the number of seconds to wait for the firmware. Once > + * this expires an error will be returned to the driver and no > + * firmware will be provided. > + * > + * Note: zero means 'wait forever'. > + **/ > +static ssize_t timeout_store(struct class *class, struct class_attribute *attr, > + const char *buf, size_t count) > +{ > + int tmp_loading_timeout = simple_strtol(buf, NULL, 10); > + > + if (tmp_loading_timeout < 0) > + tmp_loading_timeout = 0; > + > + __fw_fallback_set_timeout(tmp_loading_timeout); > + > + return count; > +} > +static CLASS_ATTR_RW(timeout); > + > +static struct attribute *firmware_class_attrs[] = { > + &class_attr_timeout.attr, > + NULL, > +}; > +ATTRIBUTE_GROUPS(firmware_class); > + > +static void fw_dev_release(struct device *dev) > +{ > + struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev); > + > + kfree(fw_sysfs); > +} > + > +#ifdef CONFIG_FW_LOADER_USER_HELPER > +static int do_firmware_uevent(struct fw_sysfs *fw_sysfs, struct kobj_uevent_env *env) > +{ > + if (add_uevent_var(env, "FIRMWARE=%s", fw_sysfs->fw_priv->fw_name)) > + return -ENOMEM; > + if (add_uevent_var(env, "TIMEOUT=%i", __firmware_loading_timeout())) > + return -ENOMEM; > + if (add_uevent_var(env, "ASYNC=%d", fw_sysfs->nowait)) > + return -ENOMEM; > + > + return 0; > +} > + > +static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env) > +{ > + struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev); > + int err = 0; > + > + mutex_lock(&fw_lock); > + if (fw_sysfs->fw_priv) > + err = do_firmware_uevent(fw_sysfs, env); > + mutex_unlock(&fw_lock); > + return err; > +} > +#endif /* CONFIG_FW_LOADER_USER_HELPER */ > + > +static struct class firmware_class = { > + .name = "firmware", > + .class_groups = firmware_class_groups, > +#ifdef CONFIG_FW_LOADER_USER_HELPER > + .dev_uevent = firmware_uevent, > +#endif > + .dev_release = fw_dev_release, > +}; > + > +int register_sysfs_loader(void) > +{ > + int ret = class_register(&firmware_class); > + > + if (ret != 0) > + return ret; > + return register_firmware_config_sysctl(); > +} > + > +void unregister_sysfs_loader(void) > +{ > + unregister_firmware_config_sysctl(); > + class_unregister(&firmware_class); > +} > + > +static ssize_t firmware_loading_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev); > + int loading = 0; > + > + mutex_lock(&fw_lock); > + if (fw_sysfs->fw_priv) > + loading = fw_sysfs_loading(fw_sysfs->fw_priv); > + mutex_unlock(&fw_lock); > + > + return sysfs_emit(buf, "%d\n", loading); > +} > + > +/** > + * firmware_loading_store() - set value in the 'loading' control file > + * @dev: device pointer > + * @attr: device attribute pointer > + * @buf: buffer to scan for loading control value > + * @count: number of bytes in @buf > + * > + * The relevant values are: > + * > + * 1: Start a load, discarding any previous partial load. > + * 0: Conclude the load and hand the data to the driver code. > + * -1: Conclude the load with an error and discard any written data. > + **/ > +static ssize_t firmware_loading_store(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev); > + struct fw_priv *fw_priv; > + ssize_t written = count; > + int loading = simple_strtol(buf, NULL, 10); > + > + mutex_lock(&fw_lock); > + fw_priv = fw_sysfs->fw_priv; > + if (fw_state_is_aborted(fw_priv) || fw_state_is_done(fw_priv)) > + goto out; > + > + switch (loading) { > + case 1: > + /* discarding any previous partial load */ > + if (!fw_sysfs_done(fw_priv)) { > + fw_free_paged_buf(fw_priv); > + fw_state_start(fw_priv); > + } > + break; > + case 0: > + if (fw_sysfs_loading(fw_priv)) { > + int rc; > + > + /* > + * Several loading requests may be pending on > + * one same firmware buf, so let all requests > + * see the mapped 'buf->data' once the loading > + * is completed. > + */ > + rc = fw_map_paged_buf(fw_priv); > + if (rc) > + dev_err(dev, "%s: map pages failed\n", > + __func__); > + else > + rc = security_kernel_post_load_data(fw_priv->data, > + fw_priv->size, > + LOADING_FIRMWARE, > + "blob"); > + > + /* > + * Same logic as fw_load_abort, only the DONE bit > + * is ignored and we set ABORT only on failure. > + */ > + if (rc) { > + fw_state_aborted(fw_priv); > + written = rc; > + } else { > + fw_state_done(fw_priv); > + } > + break; > + } > + fallthrough; > + default: > + dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading); > + fallthrough; > + case -1: > + fw_load_abort(fw_sysfs); > + break; > + } > +out: > + mutex_unlock(&fw_lock); > + return written; > +} > + > +static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store); > + > +static void firmware_rw_data(struct fw_priv *fw_priv, char *buffer, > + loff_t offset, size_t count, bool read) > +{ > + if (read) > + memcpy(buffer, fw_priv->data + offset, count); > + else > + memcpy(fw_priv->data + offset, buffer, count); > +} > + > +static void firmware_rw(struct fw_priv *fw_priv, char *buffer, > + loff_t offset, size_t count, bool read) > +{ > + while (count) { > + void *page_data; > + int page_nr = offset >> PAGE_SHIFT; > + int page_ofs = offset & (PAGE_SIZE - 1); > + int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count); > + > + page_data = kmap(fw_priv->pages[page_nr]); > + > + if (read) > + memcpy(buffer, page_data + page_ofs, page_cnt); > + else > + memcpy(page_data + page_ofs, buffer, page_cnt); > + > + kunmap(fw_priv->pages[page_nr]); > + buffer += page_cnt; > + offset += page_cnt; > + count -= page_cnt; > + } > +} > + > +static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj, > + struct bin_attribute *bin_attr, > + char *buffer, loff_t offset, size_t count) > +{ > + struct device *dev = kobj_to_dev(kobj); > + struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev); > + struct fw_priv *fw_priv; > + ssize_t ret_count; > + > + mutex_lock(&fw_lock); > + fw_priv = fw_sysfs->fw_priv; > + if (!fw_priv || fw_sysfs_done(fw_priv)) { > + ret_count = -ENODEV; > + goto out; > + } > + if (offset > fw_priv->size) { > + ret_count = 0; > + goto out; > + } > + if (count > fw_priv->size - offset) > + count = fw_priv->size - offset; > + > + ret_count = count; > + > + if (fw_priv->data) > + firmware_rw_data(fw_priv, buffer, offset, count, true); > + else > + firmware_rw(fw_priv, buffer, offset, count, true); > + > +out: > + mutex_unlock(&fw_lock); > + return ret_count; > +} > + > +static int fw_realloc_pages(struct fw_sysfs *fw_sysfs, int min_size) > +{ > + int err; > + > + err = fw_grow_paged_buf(fw_sysfs->fw_priv, > + PAGE_ALIGN(min_size) >> PAGE_SHIFT); > + if (err) > + fw_load_abort(fw_sysfs); > + return err; > +} > + > +/** > + * firmware_data_write() - write method for firmware > + * @filp: open sysfs file > + * @kobj: kobject for the device > + * @bin_attr: bin_attr structure > + * @buffer: buffer being written > + * @offset: buffer offset for write in total data store area > + * @count: buffer size > + * > + * Data written to the 'data' attribute will be later handed to > + * the driver as a firmware image. > + **/ > +static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj, > + struct bin_attribute *bin_attr, > + char *buffer, loff_t offset, size_t count) > +{ > + struct device *dev = kobj_to_dev(kobj); > + struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev); > + struct fw_priv *fw_priv; > + ssize_t retval; > + > + if (!capable(CAP_SYS_RAWIO)) > + return -EPERM; > + > + mutex_lock(&fw_lock); > + fw_priv = fw_sysfs->fw_priv; > + if (!fw_priv || fw_sysfs_done(fw_priv)) { > + retval = -ENODEV; > + goto out; > + } > + > + if (fw_priv->data) { > + if (offset + count > fw_priv->allocated_size) { > + retval = -ENOMEM; > + goto out; > + } > + firmware_rw_data(fw_priv, buffer, offset, count, false); > + retval = count; > + } else { > + retval = fw_realloc_pages(fw_sysfs, offset + count); > + if (retval) > + goto out; > + > + retval = count; > + firmware_rw(fw_priv, buffer, offset, count, false); > + } > + > + fw_priv->size = max_t(size_t, offset + count, fw_priv->size); > +out: > + mutex_unlock(&fw_lock); > + return retval; > +} > + > +static struct bin_attribute firmware_attr_data = { > + .attr = { .name = "data", .mode = 0644 }, > + .size = 0, > + .read = firmware_data_read, > + .write = firmware_data_write, > +}; > + > +static struct attribute *fw_dev_attrs[] = { > + &dev_attr_loading.attr, > + NULL > +}; > + > +static struct bin_attribute *fw_dev_bin_attrs[] = { > + &firmware_attr_data, > + NULL > +}; > + > +static const struct attribute_group fw_dev_attr_group = { > + .attrs = fw_dev_attrs, > + .bin_attrs = fw_dev_bin_attrs, > +}; > + > +static const struct attribute_group *fw_dev_attr_groups[] = { > + &fw_dev_attr_group, > + NULL > +}; > + > +struct fw_sysfs * > +fw_create_instance(struct firmware *firmware, const char *fw_name, > + struct device *device, u32 opt_flags) > +{ > + struct fw_sysfs *fw_sysfs; > + struct device *f_dev; > + > + fw_sysfs = kzalloc(sizeof(*fw_sysfs), GFP_KERNEL); > + if (!fw_sysfs) { > + fw_sysfs = ERR_PTR(-ENOMEM); > + goto exit; > + } > + > + fw_sysfs->nowait = !!(opt_flags & FW_OPT_NOWAIT); > + fw_sysfs->fw = firmware; > + f_dev = &fw_sysfs->dev; > + > + device_initialize(f_dev); > + dev_set_name(f_dev, "%s", fw_name); > + f_dev->parent = device; > + f_dev->class = &firmware_class; > + f_dev->groups = fw_dev_attr_groups; > +exit: > + return fw_sysfs; > +} > diff --git a/drivers/base/firmware_loader/sysfs.h b/drivers/base/firmware_loader/sysfs.h > new file mode 100644 > index 000000000000..5e2aff7bf6e7 > --- /dev/null > +++ b/drivers/base/firmware_loader/sysfs.h > @@ -0,0 +1,96 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#ifndef __FIRMWARE_SYSFS_H > +#define __FIRMWARE_SYSFS_H > + > +#include > + > +MODULE_IMPORT_NS(FIRMWARE_LOADER_PRIVATE); > + > +extern struct firmware_fallback_config fw_fallback_config; > + > +#ifdef CONFIG_FW_LOADER_USER_HELPER > +/** > + * struct firmware_fallback_config - firmware fallback configuration settings > + * > + * Helps describe and fine tune the fallback mechanism. > + * > + * @force_sysfs_fallback: force the sysfs fallback mechanism to be used > + * as if one had enabled CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y. > + * Useful to help debug a CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y > + * functionality on a kernel where that config entry has been disabled. > + * @ignore_sysfs_fallback: force to disable the sysfs fallback mechanism. > + * This emulates the behaviour as if we had set the kernel > + * config CONFIG_FW_LOADER_USER_HELPER=n. > + * @old_timeout: for internal use > + * @loading_timeout: the timeout to wait for the fallback mechanism before > + * giving up, in seconds. > + */ > +struct firmware_fallback_config { > + unsigned int force_sysfs_fallback; > + unsigned int ignore_sysfs_fallback; > + int old_timeout; > + int loading_timeout; > +}; > + > +int register_sysfs_loader(void); > +void unregister_sysfs_loader(void); > +#ifdef CONFIG_SYSCTL > +int register_firmware_config_sysctl(void); > +void unregister_firmware_config_sysctl(void); > +#else > +static inline int register_firmware_config_sysctl(void) > +{ > + return 0; > +} > + > +static inline void unregister_firmware_config_sysctl(void) { } > +#endif /* CONFIG_SYSCTL */ > +#else /* CONFIG_FW_LOADER_USER_HELPER */ > +static inline int register_sysfs_loader(void) > +{ > + return 0; > +} > + > +static inline void unregister_sysfs_loader(void) > +{ > +} > +#endif /* CONFIG_FW_LOADER_USER_HELPER */ > + > +struct fw_sysfs { > + bool nowait; > + struct device dev; > + struct fw_priv *fw_priv; > + struct firmware *fw; > +}; > + > +static inline struct fw_sysfs *to_fw_sysfs(struct device *dev) > +{ > + return container_of(dev, struct fw_sysfs, dev); > +} > + > +/* These getters are vetted to use int properly */ > +static inline int __firmware_loading_timeout(void) > +{ > + return fw_fallback_config.loading_timeout; > +} > + > +/* These setters are vetted to use int properly */ > +static inline void __fw_fallback_set_timeout(int timeout) > +{ > + fw_fallback_config.loading_timeout = timeout; > +} > + > +void __fw_load_abort(struct fw_priv *fw_priv); > + > +static inline void fw_load_abort(struct fw_sysfs *fw_sysfs) > +{ > + struct fw_priv *fw_priv = fw_sysfs->fw_priv; > + > + __fw_load_abort(fw_priv); > +} > + > +struct fw_sysfs * > +fw_create_instance(struct firmware *firmware, const char *fw_name, > + struct device *device, u32 opt_flags); > + > +#endif /* __FIRMWARE_SYSFS_H */