Received: by 2002:a25:683:0:0:0:0:0 with SMTP id 125csp47052ybg; Tue, 9 Jun 2020 16:00:22 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwD2Y0r6ieKWVny0DaTFfasno1C6H4QkbwUf8FwxqwjQ0LmAIUb92uu2d387XnaLawG+Dmt X-Received: by 2002:a17:906:1841:: with SMTP id w1mr608132eje.21.1591743622462; Tue, 09 Jun 2020 16:00:22 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1591743622; cv=none; d=google.com; s=arc-20160816; b=RoRe6VPBRDvdQDNnhbh+zQnnwWjhb2kW1Z8jocXt8wsjn16e0KYybA37PKSJtclhjG zLsC316CZf0874Wauq+Of8Wlax8mU5M/XLP2DN2PW6KLjKRc1VCUryFRnojBr+HVewF7 flAqP/FtzrWSTwLKZFRCLwha4kBZsiTAj5MMs1kvret0OpCCs3iiWo1XKlH6VVmXK3YI F2eV9kS+5dEPemva7itRc0VezJB/eBlguX4391obgaplFRWZ/ag98IH9rD17nbTW6Pwl Y28QRlbdoIEOYmDUiiCRd0gHTE/Dc4SD7Y+8ohZzNaXGyKxDWxNztXyu65fvfWiX98WW 1VgA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature; bh=CRbGyidHgu8ub4ibzKvKOJ9cFajk0nrgwC2Cw9/8ps0=; b=BQvggojBAR+g7v01B3Nx6Kp/mWkyIm0bHMPMnDKOiW86Ovrfu2AofwyWG6jrs2hATd usQvmVRGECKmz0yK0fGL4i8B/COWVhaJHARaDMI/+yD4aTxhgsee1BNxsNCSmdr84PaC 45tdlp3/+Y/X7HGbpe+CsN5A3x6ZOq0Edcqg1hUUUlW8ojzmhAqVtvV2HomslmtTZFag p+K9qXMqDlIn6xYqGYTihHcglG7rW8YMHMUcGfRvH1KHgWPTWZiTqBI/qChYKfXTgUUN c3uFG9xIgDgzkp066bqXIKY2Je6C+bRmojXzmUlNgKcFKbXg+N06lxmRYqu2CdMVzEs4 Vz3w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@broadcom.com header.s=google header.b=KRm1qp01; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=broadcom.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id e2si11220756edy.212.2020.06.09.15.59.59; Tue, 09 Jun 2020 16:00:22 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@broadcom.com header.s=google header.b=KRm1qp01; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=broadcom.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728687AbgFIW5e (ORCPT + 99 others); Tue, 9 Jun 2020 18:57:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60734 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728901AbgFIW5U (ORCPT ); Tue, 9 Jun 2020 18:57:20 -0400 Received: from mail-pf1-x441.google.com (mail-pf1-x441.google.com [IPv6:2607:f8b0:4864:20::441]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 42FD3C08C5C9 for ; Tue, 9 Jun 2020 15:57:15 -0700 (PDT) Received: by mail-pf1-x441.google.com with SMTP id 10so213903pfx.8 for ; Tue, 09 Jun 2020 15:57:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=CRbGyidHgu8ub4ibzKvKOJ9cFajk0nrgwC2Cw9/8ps0=; b=KRm1qp01HfOoqm7c7JJYTWeZfoG9kI+9Cj/eBfkVQk4V4iBR9cTQUZj2WVPX2uOU3T VVQF1fV1BPhqmlHv21M2DVPdhfNUW5BJg3VbHw9rh4/ejmE23l6/DQZWJH19aLWViLeQ jDnsiYPOwAXJ4KT0qeyYF/PXdrtO/oS1C3puQ= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=CRbGyidHgu8ub4ibzKvKOJ9cFajk0nrgwC2Cw9/8ps0=; b=m2oMH7Bf+NQeaSn6ttk4NwTYq8yJoO4Vby44ubsVuKnyOoex212QiUfZekidi1wcxi /5lJR9n4oQeBozv2BWaSc1Zj0weldgYkE4AmGtATDxgmOYNP5DEouMifXDKDsAaErLFB jbZDhsLAHF6PN8FY5n26McV3zq6+Pp9ESm5ccxlxFZ4CxooYEH8whD1OEaeiCILHm3hT 7orB3Nczf1D+cmfHQiCFZ0WBb4gVoSyh/3e+shDXM1l87D8/B9opzZycUxrO6NABq+1Y Kn5FhiKXyejahcDi14W7Hjsi4MilFnP6ZUVDcNR0ndtas7bYGoyFcw9kyDxnu1bm5wtd D31A== X-Gm-Message-State: AOAM532it2R8RajLPdzY+I1KKpvyPWErdXJwDIZYcF2Srkv0ow+thbNW WZHAm/0G07HEt05jCicbdRZQWQ== X-Received: by 2002:a62:7bcd:: with SMTP id w196mr84242pfc.73.1591743434616; Tue, 09 Jun 2020 15:57:14 -0700 (PDT) Received: from lbrmn-lnxub113.broadcom.net ([192.19.228.250]) by smtp.gmail.com with ESMTPSA id p8sm9104978pgs.29.2020.06.09.15.57.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jun 2020 15:57:13 -0700 (PDT) From: Scott Branden To: Luis Chamberlain , Wolfram Sang , Greg Kroah-Hartman , David Brown , Alexander Viro , Shuah Khan , bjorn.andersson@linaro.org, Shuah Khan , Arnd Bergmann Cc: Mimi Zohar , "Rafael J . Wysocki" , linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-fsdevel@vger.kernel.org, BCM Kernel Feedback , Olof Johansson , Andrew Morton , Dan Carpenter , Colin Ian King , Kees Cook , Takashi Iwai , linux-kselftest@vger.kernel.org, Andy Gross , linux-integrity@vger.kernel.org, linux-security-module@vger.kernel.org, Scott Branden Subject: [PATCH v8 2/8] firmware: add offset to request_firmware_into_buf Date: Tue, 9 Jun 2020 15:56:50 -0700 Message-Id: <20200609225656.18663-3-scott.branden@broadcom.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200609225656.18663-1-scott.branden@broadcom.com> References: <20200609225656.18663-1-scott.branden@broadcom.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add offset to request_firmware_into_buf to allow for portions of firmware file to be read into a buffer. Necessary where firmware needs to be loaded in portions from file in memory constrained systems. Signed-off-by: Scott Branden --- drivers/base/firmware_loader/firmware.h | 5 ++ drivers/base/firmware_loader/main.c | 79 +++++++++++++++++++------ include/linux/firmware.h | 12 ++++ 3 files changed, 79 insertions(+), 17 deletions(-) diff --git a/drivers/base/firmware_loader/firmware.h b/drivers/base/firmware_loader/firmware.h index 933e2192fbe8..b5487f66dc45 100644 --- a/drivers/base/firmware_loader/firmware.h +++ b/drivers/base/firmware_loader/firmware.h @@ -32,6 +32,8 @@ * @FW_OPT_FALLBACK_PLATFORM: Enable fallback to device fw copy embedded in * the platform's main firmware. If both this fallback and the sysfs * fallback are enabled, then this fallback will be tried first. + * @FW_OPT_PARTIAL: Allow partial read of firmware instead of needing to read + * entire file. */ enum fw_opt { FW_OPT_UEVENT = BIT(0), @@ -41,6 +43,7 @@ enum fw_opt { FW_OPT_NOCACHE = BIT(4), FW_OPT_NOFALLBACK_SYSFS = BIT(5), FW_OPT_FALLBACK_PLATFORM = BIT(6), + FW_OPT_PARTIAL = BIT(7), }; enum fw_status { @@ -68,6 +71,8 @@ struct fw_priv { void *data; size_t size; size_t allocated_size; + size_t offset; + u32 opt_flags; #ifdef CONFIG_FW_LOADER_PAGED_BUF bool is_paged_buf; struct page **pages; diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index ca871b13524e..82c29c1f85c6 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -167,7 +167,10 @@ static int fw_cache_piggyback_on_request(const char *name); static struct fw_priv *__allocate_fw_priv(const char *fw_name, struct firmware_cache *fwc, - void *dbuf, size_t size) + void *dbuf, + size_t size, + size_t offset, + u32 opt_flags) { struct fw_priv *fw_priv; @@ -185,6 +188,8 @@ static struct fw_priv *__allocate_fw_priv(const char *fw_name, fw_priv->fwc = fwc; fw_priv->data = dbuf; fw_priv->allocated_size = size; + fw_priv->offset = offset; + fw_priv->opt_flags = opt_flags; fw_state_init(fw_priv); #ifdef CONFIG_FW_LOADER_USER_HELPER INIT_LIST_HEAD(&fw_priv->pending_list); @@ -209,8 +214,11 @@ static struct fw_priv *__lookup_fw_priv(const char *fw_name) /* Returns 1 for batching firmware requests with the same name */ static int alloc_lookup_fw_priv(const char *fw_name, struct firmware_cache *fwc, - struct fw_priv **fw_priv, void *dbuf, - size_t size, u32 opt_flags) + struct fw_priv **fw_priv, + void *dbuf, + size_t size, + size_t offset, + u32 opt_flags) { struct fw_priv *tmp; @@ -226,7 +234,7 @@ static int alloc_lookup_fw_priv(const char *fw_name, } } - tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size); + tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size, offset, opt_flags); if (tmp) { INIT_LIST_HEAD(&tmp->list); if (!(opt_flags & FW_OPT_NOCACHE)) @@ -472,7 +480,11 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv, /* Already populated data member means we're loading into a buffer */ if (!decompress && fw_priv->data) { buffer = fw_priv->data; - id = READING_FIRMWARE_PREALLOC_BUFFER; + if (fw_priv->opt_flags & FW_OPT_PARTIAL) + id = READING_FIRMWARE_PARTIAL_READ; + else + id = READING_FIRMWARE_PREALLOC_BUFFER; + msize = fw_priv->allocated_size; } @@ -495,8 +507,10 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv, fw_priv->size = 0; /* load firmware files from the mount namespace of init */ - rc = kernel_read_file_from_path_initns(path, &buffer, - &size, msize, id); + rc = kernel_pread_file_from_path_initns(path, &buffer, + &size, msize, + fw_priv->offset, + id); if (rc) { if (rc != -ENOENT) dev_warn(device, "loading %s failed with error %d\n", @@ -683,7 +697,7 @@ int assign_fw(struct firmware *fw, struct device *device, u32 opt_flags) static int _request_firmware_prepare(struct firmware **firmware_p, const char *name, struct device *device, void *dbuf, size_t size, - u32 opt_flags) + size_t offset, u32 opt_flags) { struct firmware *firmware; struct fw_priv *fw_priv; @@ -702,7 +716,7 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name, } ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, dbuf, size, - opt_flags); + offset, opt_flags); /* * bind with 'priv' now to avoid warning in failure path @@ -749,7 +763,7 @@ static void fw_abort_batch_reqs(struct firmware *fw) static int _request_firmware(const struct firmware **firmware_p, const char *name, struct device *device, void *buf, size_t size, - u32 opt_flags) + size_t offset, u32 opt_flags) { struct firmware *fw = NULL; int ret; @@ -763,7 +777,7 @@ _request_firmware(const struct firmware **firmware_p, const char *name, } ret = _request_firmware_prepare(&fw, name, device, buf, size, - opt_flags); + offset, opt_flags); if (ret <= 0) /* error or already assigned */ goto out; @@ -825,7 +839,7 @@ request_firmware(const struct firmware **firmware_p, const char *name, /* Need to pin this module until return */ __module_get(THIS_MODULE); - ret = _request_firmware(firmware_p, name, device, NULL, 0, + ret = _request_firmware(firmware_p, name, device, NULL, 0, 0, FW_OPT_UEVENT); module_put(THIS_MODULE); return ret; @@ -852,7 +866,7 @@ int firmware_request_nowarn(const struct firmware **firmware, const char *name, /* Need to pin this module until return */ __module_get(THIS_MODULE); - ret = _request_firmware(firmware, name, device, NULL, 0, + ret = _request_firmware(firmware, name, device, NULL, 0, 0, FW_OPT_UEVENT | FW_OPT_NO_WARN); module_put(THIS_MODULE); return ret; @@ -876,7 +890,7 @@ int request_firmware_direct(const struct firmware **firmware_p, int ret; __module_get(THIS_MODULE); - ret = _request_firmware(firmware_p, name, device, NULL, 0, + ret = _request_firmware(firmware_p, name, device, NULL, 0, 0, FW_OPT_UEVENT | FW_OPT_NO_WARN | FW_OPT_NOFALLBACK_SYSFS); module_put(THIS_MODULE); @@ -901,7 +915,7 @@ int firmware_request_platform(const struct firmware **firmware, /* Need to pin this module until return */ __module_get(THIS_MODULE); - ret = _request_firmware(firmware, name, device, NULL, 0, + ret = _request_firmware(firmware, name, device, NULL, 0, 0, FW_OPT_UEVENT | FW_OPT_FALLBACK_PLATFORM); module_put(THIS_MODULE); return ret; @@ -957,13 +971,44 @@ request_firmware_into_buf(const struct firmware **firmware_p, const char *name, return -EOPNOTSUPP; __module_get(THIS_MODULE); - ret = _request_firmware(firmware_p, name, device, buf, size, + ret = _request_firmware(firmware_p, name, device, buf, size, 0, FW_OPT_UEVENT | FW_OPT_NOCACHE); module_put(THIS_MODULE); return ret; } EXPORT_SYMBOL(request_firmware_into_buf); +/** + * request_partial_firmware_into_buf() - load partial firmware into a previously allocated buffer + * @firmware_p: pointer to firmware image + * @name: name of firmware file + * @device: device for which firmware is being loaded and DMA region allocated + * @buf: address of buffer to load firmware into + * @size: size of buffer + * @offset: offset into file to read + * + * This function works pretty much like request_firmware_into_buf except + * it allows a partial read of the file. + */ +int +request_partial_firmware_into_buf(const struct firmware **firmware_p, + const char *name, struct device *device, + void *buf, size_t size, size_t offset) +{ + int ret; + + if (fw_cache_is_setup(device, name)) + return -EOPNOTSUPP; + + __module_get(THIS_MODULE); + ret = _request_firmware(firmware_p, name, device, buf, size, offset, + FW_OPT_UEVENT | FW_OPT_NOCACHE | + FW_OPT_PARTIAL); + module_put(THIS_MODULE); + return ret; +} +EXPORT_SYMBOL(request_partial_firmware_into_buf); + /** * release_firmware() - release the resource associated with a firmware image * @fw: firmware resource to release @@ -996,7 +1041,7 @@ static void request_firmware_work_func(struct work_struct *work) fw_work = container_of(work, struct firmware_work, work); - _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, + _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, 0, fw_work->opt_flags); fw_work->cont(fw, fw_work->context); put_device(fw_work->device); /* taken in request_firmware_nowait() */ diff --git a/include/linux/firmware.h b/include/linux/firmware.h index cb3e2c06ed8a..63edc2b02e4f 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -53,6 +53,9 @@ int request_firmware_direct(const struct firmware **fw, const char *name, struct device *device); int request_firmware_into_buf(const struct firmware **firmware_p, const char *name, struct device *device, void *buf, size_t size); +int request_partial_firmware_into_buf(const struct firmware **firmware_p, + const char *name, struct device *device, + void *buf, size_t size, size_t offset); void release_firmware(const struct firmware *fw); #else @@ -102,6 +105,15 @@ static inline int request_firmware_into_buf(const struct firmware **firmware_p, return -EINVAL; } +static inline int request_partial_firmware_into_buf + (const struct firmware **firmware_p, + const char *name, + struct device *device, + void *buf, size_t size, offset) +{ + return -EINVAL; +} + #endif int firmware_request_cache(struct device *device, const char *name); -- 2.17.1