mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

Updates for the kernel's CRC (cyclic redundancy check) code: - Reorganize the architecture-optimized CRC code. It now lives in lib/crc/$(SRCARCH)/ rather than arch/$(SRCARCH)/lib/, and it is no longer artificially split into separate generic and arch modules. This allows better inlining and dead code elimination. The generic CRC code is also no longer exported, simplifying the API. (This mirrors the similar changes to SHA-1 and SHA-2 in lib/crypto/, which can be found in the "Crypto library updates" pull request.) - Improve crc32c() performance on newer x86_64 CPUs on long messages by enabling the VPCLMULQDQ optimized code. - Simplify the crypto_shash wrappers for crc32_le() and crc32c(). Register just one shash algorithm for each that uses the (fully optimized) library functions, instead of unnecessarily providing direct access to the generic CRC code. - Remove unused and obsolete drivers for hardware CRC engines. - Remove CRC-32 combination functions that are no longer used. - Add kerneldoc for crc32_le(), crc32_be(), and crc32c(). - Convert the crc32() macro to an inline function. -----BEGIN PGP SIGNATURE----- iIoEABYIADIWIQSacvsUNc7UX4ntmEPzXCl4vpKOKwUCaIZ8rRQcZWJpZ2dlcnNA a2VybmVsLm9yZwAKCRDzXCl4vpKOK3yOAP9OuoCirD42ZHNSgQeGTzhhZ2jCHiPN BPvHChwtE2MSRwEA0ddNX36aOiEKmpjog3TMllOIBz7wBrwZV7KgoX75+AU= =uAY8 -----END PGP SIGNATURE----- Merge tag 'crc-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux Pull CRC updates from Eric Biggers: - Reorganize the architecture-optimized CRC code It now lives in lib/crc/$(SRCARCH)/ rather than arch/$(SRCARCH)/lib/, and it is no longer artificially split into separate generic and arch modules. This allows better inlining and dead code elimination The generic CRC code is also no longer exported, simplifying the API. (This mirrors the similar changes to SHA-1 and SHA-2 in lib/crypto/, which can be found in the "Crypto library updates" pull request) - Improve crc32c() performance on newer x86_64 CPUs on long messages by enabling the VPCLMULQDQ optimized code - Simplify the crypto_shash wrappers for crc32_le() and crc32c() Register just one shash algorithm for each that uses the (fully optimized) library functions, instead of unnecessarily providing direct access to the generic CRC code - Remove unused and obsolete drivers for hardware CRC engines - Remove CRC-32 combination functions that are no longer used - Add kerneldoc for crc32_le(), crc32_be(), and crc32c() - Convert the crc32() macro to an inline function * tag 'crc-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux: (26 commits) lib/crc: x86/crc32c: Enable VPCLMULQDQ optimization where beneficial lib/crc: x86: Reorganize crc-pclmul static_call initialization lib/crc: crc64: Add include/linux/crc64.h to kernel-api.rst lib/crc: crc32: Change crc32() from macro to inline function and remove cast nvmem: layouts: Switch from crc32() to crc32_le() lib/crc: crc32: Document crc32_le(), crc32_be(), and crc32c() lib/crc: Explicitly include <linux/export.h> lib/crc: Remove ARCH_HAS_* kconfig symbols lib/crc: x86: Migrate optimized CRC code into lib/crc/ lib/crc: sparc: Migrate optimized CRC code into lib/crc/ lib/crc: s390: Migrate optimized CRC code into lib/crc/ lib/crc: riscv: Migrate optimized CRC code into lib/crc/ lib/crc: powerpc: Migrate optimized CRC code into lib/crc/ lib/crc: mips: Migrate optimized CRC code into lib/crc/ lib/crc: loongarch: Migrate optimized CRC code into lib/crc/ lib/crc: arm64: Migrate optimized CRC code into lib/crc/ lib/crc: arm: Migrate optimized CRC code into lib/crc/ lib/crc: Prepare for arch-optimized code in subdirs of lib/crc/ lib/crc: Move files into lib/crc/ lib/crc32: Remove unused combination support ...
211 lines
5.1 KiB
C
211 lines
5.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (C) 2022 - 2023 Rafał Miłecki <rafal@milecki.pl>
|
|
*/
|
|
|
|
#include <linux/crc32.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/export.h>
|
|
#include <linux/if_ether.h>
|
|
#include <linux/nvmem-consumer.h>
|
|
#include <linux/nvmem-provider.h>
|
|
#include <linux/of.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include "u-boot-env.h"
|
|
|
|
struct u_boot_env_image_single {
|
|
__le32 crc32;
|
|
uint8_t data[];
|
|
} __packed;
|
|
|
|
struct u_boot_env_image_redundant {
|
|
__le32 crc32;
|
|
u8 mark;
|
|
uint8_t data[];
|
|
} __packed;
|
|
|
|
struct u_boot_env_image_broadcom {
|
|
__le32 magic;
|
|
__le32 len;
|
|
__le32 crc32;
|
|
DECLARE_FLEX_ARRAY(uint8_t, data);
|
|
} __packed;
|
|
|
|
static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, int index,
|
|
unsigned int offset, void *buf, size_t bytes)
|
|
{
|
|
u8 mac[ETH_ALEN];
|
|
|
|
if (bytes != MAC_ADDR_STR_LEN)
|
|
return -EINVAL;
|
|
|
|
if (!mac_pton(buf, mac))
|
|
return -EINVAL;
|
|
|
|
if (index)
|
|
eth_addr_add(mac, index);
|
|
|
|
ether_addr_copy(buf, mac);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int u_boot_env_parse_cells(struct device *dev, struct nvmem_device *nvmem, uint8_t *buf,
|
|
size_t data_offset, size_t data_len)
|
|
{
|
|
char *data = buf + data_offset;
|
|
char *var, *value, *eq;
|
|
|
|
for (var = data;
|
|
var < data + data_len && *var;
|
|
var = value + strlen(value) + 1) {
|
|
struct nvmem_cell_info info = {};
|
|
|
|
eq = strchr(var, '=');
|
|
if (!eq)
|
|
break;
|
|
*eq = '\0';
|
|
value = eq + 1;
|
|
|
|
info.name = devm_kstrdup(dev, var, GFP_KERNEL);
|
|
if (!info.name)
|
|
return -ENOMEM;
|
|
info.offset = data_offset + value - data;
|
|
info.bytes = strlen(value);
|
|
info.np = of_get_child_by_name(dev->of_node, info.name);
|
|
if (!strcmp(var, "ethaddr")) {
|
|
info.raw_len = strlen(value);
|
|
info.bytes = ETH_ALEN;
|
|
info.read_post_process = u_boot_env_read_post_process_ethaddr;
|
|
}
|
|
|
|
nvmem_add_one_cell(nvmem, &info);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int u_boot_env_parse(struct device *dev, struct nvmem_device *nvmem,
|
|
enum u_boot_env_format format)
|
|
{
|
|
size_t crc32_data_offset;
|
|
size_t crc32_data_len;
|
|
size_t crc32_offset;
|
|
uint32_t *crc32_addr;
|
|
size_t data_offset;
|
|
size_t data_len;
|
|
size_t dev_size;
|
|
uint32_t crc32;
|
|
uint32_t calc;
|
|
uint8_t *buf;
|
|
int bytes;
|
|
int err;
|
|
|
|
dev_size = nvmem_dev_size(nvmem);
|
|
|
|
buf = kzalloc(dev_size, GFP_KERNEL);
|
|
if (!buf) {
|
|
err = -ENOMEM;
|
|
goto err_out;
|
|
}
|
|
|
|
bytes = nvmem_device_read(nvmem, 0, dev_size, buf);
|
|
if (bytes < 0) {
|
|
err = bytes;
|
|
goto err_kfree;
|
|
} else if (bytes != dev_size) {
|
|
err = -EIO;
|
|
goto err_kfree;
|
|
}
|
|
|
|
switch (format) {
|
|
case U_BOOT_FORMAT_SINGLE:
|
|
crc32_offset = offsetof(struct u_boot_env_image_single, crc32);
|
|
crc32_data_offset = offsetof(struct u_boot_env_image_single, data);
|
|
data_offset = offsetof(struct u_boot_env_image_single, data);
|
|
break;
|
|
case U_BOOT_FORMAT_REDUNDANT:
|
|
crc32_offset = offsetof(struct u_boot_env_image_redundant, crc32);
|
|
crc32_data_offset = offsetof(struct u_boot_env_image_redundant, data);
|
|
data_offset = offsetof(struct u_boot_env_image_redundant, data);
|
|
break;
|
|
case U_BOOT_FORMAT_BROADCOM:
|
|
crc32_offset = offsetof(struct u_boot_env_image_broadcom, crc32);
|
|
crc32_data_offset = offsetof(struct u_boot_env_image_broadcom, data);
|
|
data_offset = offsetof(struct u_boot_env_image_broadcom, data);
|
|
break;
|
|
}
|
|
|
|
if (dev_size < data_offset) {
|
|
dev_err(dev, "Device too small for u-boot-env\n");
|
|
err = -EIO;
|
|
goto err_kfree;
|
|
}
|
|
|
|
crc32_addr = (uint32_t *)(buf + crc32_offset);
|
|
crc32 = *crc32_addr;
|
|
crc32_data_len = dev_size - crc32_data_offset;
|
|
data_len = dev_size - data_offset;
|
|
|
|
calc = crc32_le(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L;
|
|
if (calc != crc32) {
|
|
dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32);
|
|
err = -EINVAL;
|
|
goto err_kfree;
|
|
}
|
|
|
|
buf[dev_size - 1] = '\0';
|
|
err = u_boot_env_parse_cells(dev, nvmem, buf, data_offset, data_len);
|
|
|
|
err_kfree:
|
|
kfree(buf);
|
|
err_out:
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(u_boot_env_parse);
|
|
|
|
static int u_boot_env_add_cells(struct nvmem_layout *layout)
|
|
{
|
|
struct device *dev = &layout->dev;
|
|
enum u_boot_env_format format;
|
|
|
|
format = (uintptr_t)device_get_match_data(dev);
|
|
|
|
return u_boot_env_parse(dev, layout->nvmem, format);
|
|
}
|
|
|
|
static int u_boot_env_probe(struct nvmem_layout *layout)
|
|
{
|
|
layout->add_cells = u_boot_env_add_cells;
|
|
|
|
return nvmem_layout_register(layout);
|
|
}
|
|
|
|
static void u_boot_env_remove(struct nvmem_layout *layout)
|
|
{
|
|
nvmem_layout_unregister(layout);
|
|
}
|
|
|
|
static const struct of_device_id u_boot_env_of_match_table[] = {
|
|
{ .compatible = "u-boot,env", .data = (void *)U_BOOT_FORMAT_SINGLE, },
|
|
{ .compatible = "u-boot,env-redundant-bool", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
|
|
{ .compatible = "u-boot,env-redundant-count", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
|
|
{ .compatible = "brcm,env", .data = (void *)U_BOOT_FORMAT_BROADCOM, },
|
|
{},
|
|
};
|
|
|
|
static struct nvmem_layout_driver u_boot_env_layout = {
|
|
.driver = {
|
|
.name = "u-boot-env-layout",
|
|
.of_match_table = u_boot_env_of_match_table,
|
|
},
|
|
.probe = u_boot_env_probe,
|
|
.remove = u_boot_env_remove,
|
|
};
|
|
module_nvmem_layout_driver(u_boot_env_layout);
|
|
|
|
MODULE_AUTHOR("Rafał Miłecki");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DEVICE_TABLE(of, u_boot_env_of_match_table);
|
|
MODULE_DESCRIPTION("NVMEM layout driver for U-Boot environment variables");
|