Index: sys/compat/linuxkpi/common/include/asm/unaligned.h =================================================================== --- /dev/null +++ sys/compat/linuxkpi/common/include/asm/unaligned.h @@ -0,0 +1,78 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 The FreeBSD Foundation + * + * This software was developed by Björn Zeeb under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _ASM_UNALIGNED_H +#define _ASM_UNALIGNED_H + +#include +#include + +static __inline uint32_t +get_unaligned_le32(const void *p) +{ + + return (le32_to_cpup((const __le32 *)p)); +} + +static __inline void +put_unaligned_le32(__le32 v, void *p) +{ + __le32 x; + + x = cpu_to_le32(v); + memcpy(p, &x, sizeof(x)); +} + +static __inline void +put_unaligned_le64(__le64 v, void *p) +{ + __le64 x; + + x = cpu_to_le64(v); + memcpy(p, &x, sizeof(x)); +} + +static __inline uint16_t +get_unaligned_be16(const void *p) +{ + + return (be16_to_cpup((const __be16 *)p)); +} + +static __inline uint32_t +get_unaligned_be32(const void *p) +{ + + return (be32_to_cpup((const __be32 *)p)); +} + +#endif /* _ASM_UNALIGNED_H */ Index: sys/compat/linuxkpi/common/include/linux/device.h =================================================================== --- sys/compat/linuxkpi/common/include/linux/device.h +++ sys/compat/linuxkpi/common/include/linux/device.h @@ -559,4 +559,16 @@ char *kvasprintf(gfp_t, const char *, va_list); char *kasprintf(gfp_t, const char *, ...); +/* XXX-BZ whatever they do with the _dev, I don't know yet. */ +/* XXX-BZ they seem to track allocated resources and auto-free them. */ +/* XXX-BZ TOD FIXME, we leak these resources, as noted by driver unload. */ +#define devm_kmalloc(_dev, _size, _gfp) \ + kmalloc((_size), (_gfp)) + +#define devm_kzalloc(_dev, _size, _gfp) \ + devm_kmalloc((_dev), (_size), (_gfp) | __GFP_ZERO) + +#define devm_kcalloc(_dev, _sizen, _size, _gfp) \ + devm_kmalloc((_dev), ((_sizen) * (_size)), (_gfp) | __GFP_ZERO) + #endif /* _LINUX_DEVICE_H_ */ Index: sys/compat/linuxkpi/common/include/linux/firmware.h =================================================================== --- /dev/null +++ sys/compat/linuxkpi/common/include/linux/firmware.h @@ -0,0 +1,87 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 The FreeBSD Foundation + * + * This software was developed by Björn Zeeb under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _LINUXKPI_LINUX_FIRMWARE_H +#define _LINUXKPI_LINUX_FIRMWARE_H + +#include +#include +#include + +struct firmware; + +struct linuxkpi_firmware { + size_t size; + const uint8_t *data; + /* XXX Does Linux expose anything else? */ + + /* This is LinuxKPI implementation private. */ + const struct firmware *fbdfw; +}; + +int linuxkpi_request_firmware_nowait(struct module *, bool, const char *, + struct device *, gfp_t, void *, + void(*cont)(const struct linuxkpi_firmware *, void *)); +int linuxkpi_request_firmware(const struct linuxkpi_firmware **, const char *, + struct device *); +void linuxkpi_release_firmware(const struct linuxkpi_firmware *); + + +static __inline int +request_firmware_nowait(struct module *mod, bool _t, + const char *fw_name, struct device *dev, gfp_t gfp, void *drv, + void(*cont)(const struct linuxkpi_firmware *, void *)) +{ + + + return (linuxkpi_request_firmware_nowait(mod, _t, fw_name, dev, gfp, + drv, cont)); +} + +static __inline int +request_firmware(const struct linuxkpi_firmware **fw, + const char *fw_name, struct device *dev) +{ + + return (linuxkpi_request_firmware(fw, fw_name, dev)); +} + +static __inline void +release_firmware(const struct linuxkpi_firmware *fw) +{ + + linuxkpi_release_firmware(fw); +} + +#define firmware linuxkpi_firmware + +#endif /* _LINUXKPI_LINUX_FIRMWARE_H */ Index: sys/compat/linuxkpi/common/include/linux/kernel.h =================================================================== --- sys/compat/linuxkpi/common/include/linux/kernel.h +++ sys/compat/linuxkpi/common/include/linux/kernel.h @@ -596,4 +596,45 @@ #define TAINT_WARN 0 #define test_taint(x) (0) +/* + * Checking if an option is defined would be easy if we could do CPP inside CPP. + * The defined case whether -Dxxx or -Dxxx=1 are easy to deal with. In either + * case the defined value is "1". A more general -Dxxx= case will require + * more effort to deal with all possible "true" values. Hope we do not have + * to do this as well. + * The real problem is the undefined case. To avoid this problem we do the + * concat/varargs trick: "yyy" ## xxx can make two arguments if xxx is "1" + * by having a #define for yyy_1 which is "ignore,". + * Otherwise we will just get "yyy". + * Need to be careful about variable substitutions in macros though. + * This way we make a (true, false) problem a (don't care, true, false) or a + * (don't care true, false). Then we can use a variadic macro to only select + * the always well known and defined argument #2. And that seems to be + * exactly what we need. + */ +#define ___XAB_1 dontcare, +#define ___IS_XAB(_ignore, _x, ...) (_x) +#define __IS_XAB(_x) ___IS_XAB(_x true, false) +#define _IS_XAB(_x) __IS_XAB(__CONCAT(___XAB_, _x)) + +/* This is if CONFIG_ccc=y. */ +#define IS_BUILTIN(_x) _IS_XAB(_x) +/* This is if CONFIG_ccc=m. */ +#define IS_MODULE(_x) _IS_XAB(_x ## _MODULE) +/* This is if CONFIG_ccc is compiled in(=y) or a module(=m). */ +#define IS_ENABLED(_x) (IS_BUILTIN(_x) || IS_MODULE(_x)) +/* + * This is weird case. If the CONFIG_ccc is builtin (=y) this returns true; + * or if the CONFIG_ccc is a module (=m) and the caller is built as a module + * (-DMODULE defined) this returns true, but if the callers is not a module + * (-DMODULE not defined, which means caller is BUILTIN) then it returns + * false. In other words, a module can reach the kernel, a module can reach + * a module, but the kernel cannot reach a module, and code never compiled + * cannot be reached either. + * XXX -- I'd hope the module-to-module case would be handled by a proper + * module dependency definition (MODULE_DEPEND() in FreeBSD). + */ +#define IS_REACHABLE(_x) (IS_BUILTIN(_x) || \ + (IS_MODULE(_x) && IS_BUILTIN(MODULE))) + #endif /* _LINUX_KERNEL_H_ */ Index: sys/compat/linuxkpi/common/include/linux/kobject.h =================================================================== --- sys/compat/linuxkpi/common/include/linux/kobject.h +++ sys/compat/linuxkpi/common/include/linux/kobject.h @@ -41,6 +41,8 @@ struct kobject; struct sysctl_oid; +#define KOBJ_CHANGE 0x01 /* XXX are there others? */ + struct kobj_type { void (*release)(struct kobject *kobj); const struct sysfs_ops *sysfs_ops; @@ -151,4 +153,9 @@ int kobject_init_and_add(struct kobject *kobj, const struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...); +static __inline void +kobject_uevent_env(struct kobject *kobj, int action, char *envp[]) +{ +} + #endif /* _LINUX_KOBJECT_H_ */ Index: sys/compat/linuxkpi/common/include/linux/lockdep.h =================================================================== --- sys/compat/linuxkpi/common/include/linux/lockdep.h +++ sys/compat/linuxkpi/common/include/linux/lockdep.h @@ -42,6 +42,7 @@ #define lockdep_set_class_and_name(lock, key, name) #define lockdep_set_current_reclaim_state(g) do { } while (0) #define lockdep_clear_current_reclaim_state() do { } while (0) +#define lockdep_init_map(_map, _name, _key, _x) do { } while(0) #ifdef INVARIANTS #define lockdep_assert_held(m) do { \ Index: sys/compat/linuxkpi/common/include/linux/pci.h =================================================================== --- sys/compat/linuxkpi/common/include/linux/pci.h +++ sys/compat/linuxkpi/common/include/linux/pci.h @@ -1058,4 +1058,20 @@ return (0); } +static __inline void +pci_dev_put(struct pci_dev *pdev) +{ + + if (pdev != NULL) + put_device(&pdev->dev); /* == pdev */ +} + +static __inline int +pci_domain_nr(struct pci_bus *bus) +{ + + /* XXX should we assert that it is 0? */ + return (bus->domain); +} + #endif /* _LINUX_PCI_H_ */ Index: sys/compat/linuxkpi/common/include/linux/pm.h =================================================================== --- /dev/null +++ sys/compat/linuxkpi/common/include/linux/pm.h @@ -0,0 +1,52 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 The FreeBSD Foundation + * + * This software was developed by Björn Zeeb under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _LINUXKPI_LINUX_PM_H +#define _LINUXKPI_LINUX_PM_H + +#ifdef CONFIG_PM_SLEEP +#define SIMPLE_DEV_PM_OPS(_name, _suspendfunc, _resumefunc) \ +const struct dev_pm_ops _name = { \ + .suspend = _suspendfunc, \ + .resume = _resumefunc, \ + .freeze = _suspendfunc, \ + .thaw = _resumefunc, \ + .poweroff = _suspendfunc, \ + .restore = _resumefunc, \ +} +#else +#define SIMPLE_DEV_PM_OPS(_name, _suspendfunc, _resumefunc) \ +const struct dev_pm_ops _name = { \ +} +#endif + +#endif /* _LINUXKPI_LINUX_PM_H */ Index: sys/compat/linuxkpi/common/include/linux/scatterlist.h =================================================================== --- sys/compat/linuxkpi/common/include/linux/scatterlist.h +++ sys/compat/linuxkpi/common/include/linux/scatterlist.h @@ -32,6 +32,9 @@ #ifndef _LINUX_SCATTERLIST_H_ #define _LINUX_SCATTERLIST_H_ +#include +#include + #include #include #include @@ -479,4 +482,55 @@ return (nth_page(sg_page(piter->sg), piter->sg_pgoffset)); } +static __inline size_t +sg_pcopy_from_buffer(struct scatterlist *sgl, unsigned int nents, + const void *buf, size_t buflen, off_t skip) +{ + struct sg_page_iter piter; + struct page *page; + struct sf_buf *sf; + size_t len, copied; + char *p, *b; + + if (buflen == 0) + return (0); + + b = __DECONST(char *, buf); + copied = 0; + sched_pin(); + for_each_sg_page(sgl, &piter, nents, 0) { + + /* Skip to the start. */ + if (piter.sg->length <= skip) { + skip -= piter.sg->length; + continue; + } + + /* See how much to copy. */ + KASSERT(((piter.sg->length - skip) != 0 && (buflen != 0)), + ("%s: sg len %u - skip %ju || buflen %zu is 0\n", + __func__, piter.sg->length, (uintmax_t)skip, buflen)); + len = min(piter.sg->length - skip, buflen); + + page = sg_page_iter_page(&piter); + sf = sf_buf_alloc(page, SFB_CPUPRIVATE | SFB_NOWAIT); + if (sf == NULL) + break; + p = (char *)sf_buf_kva(sf) + piter.sg_pgoffset + skip; + memcpy(p, b, len); + sf_buf_free(sf); + + copied += len; + /* Either we exactly filled the page, or we are done. */ + buflen -= len; + if (buflen == 0) + break; + skip -= len; + b += len; + } + sched_unpin(); + + return (copied); +} + #endif /* _LINUX_SCATTERLIST_H_ */ Index: sys/compat/linuxkpi/common/src/linux_firmware.c =================================================================== --- /dev/null +++ sys/compat/linuxkpi/common/src/linux_firmware.c @@ -0,0 +1,149 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 The FreeBSD Foundation + * + * This software was developed by Björn Zeeb under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include + +#include +#include + +#include +#undef firmware + +MALLOC_DEFINE(M_LKPI_FW, "lkpifw", "LinuxKPI Firmware"); + +static int +_linuxkpi_request_firmware(const char *fw_name, const struct linuxkpi_firmware **fw, + gfp_t gfp __unused, bool enoentok) +{ + const struct firmware *fbdfw; + struct linuxkpi_firmware *lfw; + const char *fwimg; + char *p; + + if (fw == NULL) + return (-EINVAL); + + KASSERT(gfp == GFP_KERNEL, ("%s: gfp %#x", __func__, gfp)); + lfw = malloc(sizeof(*lfw), M_LKPI_FW, M_WAITOK); + + /* + * Linux can have a path in the firmware which is hard to replicate + * for auto-firmware-module-loading. + * On FreeBSD, depending on what people do, the firmware will either + * be called "fw", or "dir_fw", or "modname_dir_fw". The later the + * driver author has to deal with herself. + * We also optionally flatten '.'s as some firmware modules do, + * while others do not. + */ + /* (1) Try any name removed of path. */ + fwimg = strrchr(fw_name, '/'); + if (fwimg != NULL) + fwimg++; + if (fwimg == NULL || *fwimg == '\0') + fwimg = fw_name; + fbdfw = firmware_get(fwimg); + /* (2) Try the original name if we have not yet. */ + if (fbdfw == NULL && fwimg != fw_name) { + fwimg = fw_name; + fbdfw = firmware_get(fwimg); + } + /* (3) Flatten '/' and then '.' to '_' and try with adjusted name. */ + if (fbdfw == NULL && + (strchr(fw_name, '/') != NULL || strchr(fw_name, '.') != NULL)) { + fwimg = strdup(fw_name, M_LKPI_FW); + if (fwimg != NULL) { + while ((p = strchr(fwimg, '/')) != NULL) + *p = '_'; + fbdfw = firmware_get(fwimg); + if (fbdfw == NULL) { + while ((p = strchr(fwimg, '.')) != NULL) + *p = '_'; + fbdfw = firmware_get(fwimg); + } + free(__DECONST(void *, fwimg), M_LKPI_FW); + } + } + if (fbdfw == NULL) { + if (enoentok) + *fw = lfw; + else + free(lfw, M_LKPI_FW); + return (-ENOENT); + } + + lfw->fbdfw = fbdfw; + lfw->data = (const uint8_t *)fbdfw->data; + lfw->size = fbdfw->datasize; + *fw = lfw; + return (0); +} + +int +linuxkpi_request_firmware_nowait(struct module *mod __unused, bool _t __unused, + const char *fw_name, struct device *dev __unused, gfp_t gfp, void *drv, + void(*cont)(const struct linuxkpi_firmware *, void *)) +{ + const struct linuxkpi_firmware *lfw; + int error; + + /* Linux seems to run the callback if it cannot find the firmware. */ + error = _linuxkpi_request_firmware(fw_name, &lfw, gfp, true); + if (error == -ENOENT) + error = 0; + /* XXX-BZ we do not run this deferred. It's a compat layer. */ + if (error == 0) + cont(lfw, drv); + + return (error); +} + +int +linuxkpi_request_firmware(const struct linuxkpi_firmware **fw, + const char *fw_name, struct device *dev __unused) +{ + + return (_linuxkpi_request_firmware(fw_name, fw, GFP_KERNEL, false)); +} + +void +linuxkpi_release_firmware(const struct linuxkpi_firmware *fw) +{ + + if (fw == NULL) + return; + + if (fw->fbdfw) + firmware_put(fw->fbdfw, FIRMWARE_UNLOAD); + free(__DECONST(void *, fw), M_LKPI_FW); +} Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -4507,6 +4507,8 @@ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_current.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" +compat/linuxkpi/common/src/linux_firmware.c optional compat_linuxkpi \ + compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_hrtimer.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_kthread.c optional compat_linuxkpi \ Index: sys/modules/linuxkpi/Makefile =================================================================== --- sys/modules/linuxkpi/Makefile +++ sys/modules/linuxkpi/Makefile @@ -4,6 +4,7 @@ KMOD= linuxkpi SRCS= linux_compat.c \ linux_current.c \ + linux_firmware.c \ linux_hrtimer.c \ linux_idr.c \ linux_kmod.c \