diff --git a/sys/conf/automation.mk b/sys/conf/automation.mk new file mode 100644 --- /dev/null +++ b/sys/conf/automation.mk @@ -0,0 +1,74 @@ +# +# SPDX-License-Identifier: BSD-2-Clause-FreeBSD +# +# Copyright (c) 2023 Hans Petter Selasky +# +# 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$ + +AUTOMATION_BASE=automation + +CLEANFILES+=\ + ${AUTOMATION_BASE}.txt \ + ${AUTOMATION_BASE}.o + +DEPENDOBJS+=\ + ${AUTOMATION_BASE}.txt \ + ${AUTOMATION_BASE}.o + +AUTOMATION_HEADER=(\ + echo "/*" ; \ + echo " * This file was automatically generated. " ; \ + echo " * Please do not edit. " ; \ + echo " */ " ; \ + echo "" ; \ + echo "\#include " ; \ + echo "\#include " ; \ + echo "" ; \ + echo "struct sysinit;" ; \ + echo "" ; \ + echo "\#define SYSINIT(var) \\" ; \ + echo "extern struct sysinit var; \\" ; \ + echo "DATA_WSET(sysinit_set,var)" ; \ + echo "" ; \ + echo "\#define SYSUNINIT(var) \\" ; \ + echo "extern struct sysinit var; \\" ; \ + echo "DATA_WSET(sysuninit_set,var)" ; \ + echo "" \ +) + +# Define hint to make global variables uniq +CFLAGS+= \ + -DAUTOMATION_HINT=${.TARGET:C/.*\///g:S/./_/g:S/-/_/g} + +${AUTOMATION_BASE}.o: ${OBJS:N${AUTOMATION_BASE}_sysinit.o} + @${LD} -T /dev/null -o ${.TARGET} -r ${.ALLSRC} + +${AUTOMATION_BASE}.txt: ${AUTOMATION_BASE}.o + @${OBJCOPY} -j set_automation -O binary ${.ALLSRC} ${.TARGET} + +${AUTOMATION_BASE}_sysinit.c: ${AUTOMATION_BASE}.txt + @ ${AUTOMATION_HEADER} > ${.TARGET} + @cat ${AUTOMATION_BASE}.txt | tr '\0' '\n' | grep -E "^sysinit " | sort -f | ${AWK} '{print "SYSINIT(" $$4 "); /* " $$2 " " $$3 " */"}' >> ${.TARGET} + @echo "" >> ${.TARGET} + @cat ${AUTOMATION_BASE}.txt | tr '\0' '\n' | grep -E "^sysuninit " | sort -fr | ${AWK} '{print "SYSUNINIT(" $$4 "); /* " $$2 " " $$3 " */"}' >> ${.TARGET} diff --git a/sys/conf/kern.mk b/sys/conf/kern.mk --- a/sys/conf/kern.mk +++ b/sys/conf/kern.mk @@ -332,6 +332,9 @@ LD_EMULATION_riscv64= elf64lriscv LD_EMULATION=${LD_EMULATION_${MACHINE_ARCH}} +# Kernel automation +.include "automation.mk" + # # Autogenerated files must be located in the current object directory # and not the one belonging to the parent build. This prevents kernel diff --git a/sys/conf/kern.post.mk b/sys/conf/kern.post.mk --- a/sys/conf/kern.post.mk +++ b/sys/conf/kern.post.mk @@ -8,6 +8,11 @@ # should be defined in the kern.pre.mk so that port makefiles can # override or augment them. +# Kernel automation +CFILES+= automation_sysinit.c +OBJS+= automation_sysinit.o +CLEAN+= automation_sysinit.c + .if defined(DTS) || defined(DTSO) || defined(FDT_DTS_FILE) .include "dtb.build.mk" diff --git a/sys/conf/kmod.mk b/sys/conf/kmod.mk --- a/sys/conf/kmod.mk +++ b/sys/conf/kmod.mk @@ -190,6 +190,10 @@ CTFFLAGS+= -g .endif +# Module automation +SRCS+= automation_sysinit.c +CLEANFILES+= automation_sysinit.c + .if defined(FIRMWS) ${KMOD:S/$/.c/}: ${SYSDIR}/tools/fw_stub.awk ${AWK} -f ${SYSDIR}/tools/fw_stub.awk ${FIRMWS} -m${KMOD} -c${KMOD:S/$/.c/g} \ diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -160,8 +160,26 @@ * If we want to register new sysinit types, add them to newsysinit. */ SET_DECLARE(sysinit_set, struct sysinit); -struct sysinit **sysinit, **sysinit_end; -struct sysinit **newsysinit, **newsysinit_end; +static struct sysinit **sysinit, **sysinit_end; +static struct sysinit **newsysinit, **newsysinit_end; + +/* + * Compare two sysinit structures. + */ +static int +sysinit_compare(struct sysinit **ppa, struct sysinit **ppb) +{ + if (ppa[0][0].subsystem > ppb[0][0].subsystem) + return (1); + else if (ppa[0][0].subsystem < ppb[0][0].subsystem) + return (-1); + else if (ppa[0][0].order > ppb[0][0].order) + return (1); + else if (ppa[0][0].order < ppb[0][0].order) + return (-1); + else + return (0); +} /* * Merge a new sysinit set into the current set, reallocating it if @@ -171,8 +189,10 @@ sysinit_add(struct sysinit **set, struct sysinit **set_end) { struct sysinit **newset; - struct sysinit **sipp; - struct sysinit **xipp; + struct sysinit **ppa; + struct sysinit **ppb; + struct sysinit **ppc; + struct sysinit **end; int count; count = set_end - set; @@ -180,24 +200,73 @@ count += newsysinit_end - newsysinit; else count += sysinit_end - sysinit; - newset = malloc(count * sizeof(*sipp), M_TEMP, M_NOWAIT); + newset = malloc(count * sizeof(*newset), M_TEMP, M_NOWAIT); if (newset == NULL) - panic("cannot malloc for sysinit"); - xipp = newset; - if (newsysinit) - for (sipp = newsysinit; sipp < newsysinit_end; sipp++) - *xipp++ = *sipp; - else - for (sipp = sysinit; sipp < sysinit_end; sipp++) - *xipp++ = *sipp; - for (sipp = set; sipp < set_end; sipp++) - *xipp++ = *sipp; - if (newsysinit) - free(newsysinit, M_TEMP); + panic("Cannot allocate memory for sysinit_add()"); + + ppa = set; + ppb = newsysinit ? newsysinit : sysinit; + end = newsysinit ? newsysinit_end : sysinit_end; + ppc = newset; + + /* Merge two sorted lists by picking the lowest element first. */ + + while (ppa != set_end && ppb != end) { + switch (sysinit_compare(ppa, ppb)) { + case -1: + *ppc++ = *ppa++; + break; + case 0: + *ppc++ = *ppa++; + *ppc++ = *ppb++; + break; + default: + *ppc++ = *ppb++; + break; + } + } + + while (ppa != set_end) + *ppc++ = *ppa++; + while (ppb != end) + *ppc++ = *ppb++; + + free(newsysinit, M_TEMP); newsysinit = newset; newsysinit_end = newset + count; } +/* + * Sort a sysinit or sysuninit set. The entries should already have + * been sorted, and this function is basically a O(N) dummy operation! + */ +void +sysinit_sort(struct sysinit **start, struct sysinit **stop) +{ + struct sysinit **sipp, **xipp, *save; + + /* check for empty set */ + if (start == stop) + return; + + TSENTER2("insertionsort"); + + /* + * Sort all entries by their subsystem (primary key) and order + * (secondary key). + */ + for (sipp = start + 1; sipp != stop; sipp++) { + for (xipp = sipp; xipp != start && + sysinit_compare(xipp - 1, xipp) > 0; xipp--) { + save = *(xipp - 1); + *(xipp - 1) = *xipp; + *xipp = save; + } + } + + TSEXIT2("insertionsort"); +} + #if defined (DDB) && defined(VERBOSE_SYSINIT) static const char * symbol_name(vm_offset_t va, db_strategy_t strategy) @@ -232,8 +301,6 @@ { struct sysinit **sipp; /* system initialization*/ - struct sysinit **xipp; /* interior loop of sort*/ - struct sysinit *save; /* bubble*/ int last; #if defined(VERBOSE_SYSINIT) @@ -251,23 +318,7 @@ } restart: - /* - * Perform a bubble sort of the system initialization objects by - * their subsystem (primary key) and order (secondary key). - */ - TSENTER2("bubblesort"); - for (sipp = sysinit; sipp < sysinit_end; sipp++) { - for (xipp = sipp + 1; xipp < sysinit_end; xipp++) { - if ((*sipp)->subsystem < (*xipp)->subsystem || - ((*sipp)->subsystem == (*xipp)->subsystem && - (*sipp)->order <= (*xipp)->order)) - continue; /* skip*/ - save = *sipp; - *sipp = *xipp; - *xipp = save; - } - } - TSEXIT2("bubblesort"); + sysinit_sort(sysinit, sysinit_end); last = SI_SUB_COPYRIGHT; #if defined(VERBOSE_SYSINIT) @@ -326,6 +377,8 @@ /* Check off the one we're just done */ last = (*sipp)->subsystem; (*sipp)->subsystem = SI_SUB_DONE; + /* Clear the order to avoid re-sorting the already executed sysinits */ + (*sipp)->order = SI_ORDER_FIRST; /* Check if we've installed more sysinit items via KLD */ if (newsysinit != NULL) { diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c --- a/sys/kern/kern_linker.c +++ b/sys/kern/kern_linker.c @@ -195,8 +195,8 @@ static void linker_file_sysinit(linker_file_t lf) { - struct sysinit **start, **stop, **sipp, **xipp, *save; - int last; + struct sysinit **start, **stop, **sipp; + uint32_t last; KLD_DPF(FILE, ("linker_file_sysinit: calling SYSINITs for %s\n", lf->filename)); @@ -205,24 +205,8 @@ if (linker_file_lookup_set(lf, "sysinit_set", &start, &stop, NULL) != 0) return; - /* - * Perform a bubble sort of the system initialization objects by - * their subsystem (primary key) and order (secondary key). - * - * Since some things care about execution order, this is the operation - * which ensures continued function. - */ - for (sipp = start; sipp < stop; sipp++) { - for (xipp = sipp + 1; xipp < stop; xipp++) { - if ((*sipp)->subsystem < (*xipp)->subsystem || - ((*sipp)->subsystem == (*xipp)->subsystem && - (*sipp)->order <= (*xipp)->order)) - continue; /* skip */ - save = *sipp; - *sipp = *xipp; - *xipp = save; - } - } + + sysinit_sort(start, stop); /* * Traverse the (now) ordered list of system initialization tasks. @@ -250,8 +234,8 @@ static void linker_file_sysuninit(linker_file_t lf) { - struct sysinit **start, **stop, **sipp, **xipp, *save; - int last; + struct sysinit **start, **stop, **sipp; + uint32_t last; KLD_DPF(FILE, ("linker_file_sysuninit: calling SYSUNINITs for %s\n", lf->filename)); @@ -262,24 +246,7 @@ NULL) != 0) return; - /* - * Perform a reverse bubble sort of the system initialization objects - * by their subsystem (primary key) and order (secondary key). - * - * Since some things care about execution order, this is the operation - * which ensures continued function. - */ - for (sipp = start; sipp < stop; sipp++) { - for (xipp = sipp + 1; xipp < stop; xipp++) { - if ((*sipp)->subsystem > (*xipp)->subsystem || - ((*sipp)->subsystem == (*xipp)->subsystem && - (*sipp)->order >= (*xipp)->order)) - continue; /* skip */ - save = *sipp; - *sipp = *xipp; - *xipp = save; - } - } + sysinit_sort(start, stop); /* * Traverse the (now) ordered list of system initialization tasks. @@ -289,12 +256,12 @@ mtx_lock(&Giant); last = SI_SUB_DUMMY; for (sipp = start; sipp < stop; sipp++) { - if ((*sipp)->subsystem == SI_SUB_DUMMY) + if ((*sipp)->subsystem == (SI_SUB_DUMMY ^ SI_SUB_LAST)) continue; /* skip dummy task(s) */ if ((*sipp)->subsystem > last) BOOTTRACE("%s: sysuninit 0x%7x", lf->filename, - (*sipp)->subsystem); + (*sipp)->subsystem ^ SI_SUB_LAST); /* Call function */ (*((*sipp)->func)) ((*sipp)->udata); diff --git a/sys/sys/automation.h b/sys/sys/automation.h new file mode 100644 --- /dev/null +++ b/sys/sys/automation.h @@ -0,0 +1,74 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2023 Hans Petter Selasky + * + * 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 _SYS_AUTOMATION_H_ +#define _SYS_AUTOMATION_H_ + +#include + +__WEAK(__start_set_automation); +__WEAK(__stop_set_automation); + +#define __EXPORT_AUTOMATION(id,...) \ +static const id __section("set_automation") __used __aligned(1) = { __VA_ARGS__ } + +/* + * The EXPORT_AUTOMATION() macro stores the C-string after the first + * identifier argument, into a special automation section. The strings + * in this section are later parsed by "sys/conf/automation.mk" . + * + * All NUL-characters are converted into newline characters. + * + * There is one command per line. + * + * Typical format: + * "commandargument(s)\n". + * + * Example: + * EXPORT_AUTOMATION(char xxx[], "sysinit global_variable"); + */ +#define EXPORT_AUTOMATION(...) \ + __EXPORT_AUTOMATION(__VA_ARGS__) + +#define EXPORT_HEX_DIGIT(x) \ + (((x) < 10) ? ('0' + (x)) : ('a' + (x) - 10)) + +#define EXPORT_HEX_U8(x) \ + EXPORT_HEX_DIGIT(((x) >> 4) & 0xF), \ + EXPORT_HEX_DIGIT(((x) >> 0) & 0xF) + +#define EXPORT_HEX_U16(x) \ + EXPORT_HEX_U8((x) >> 8), \ + EXPORT_HEX_U8((x) >> 0) + +#define EXPORT_HEX_U32(x) \ + EXPORT_HEX_U16((x) >> 16), \ + EXPORT_HEX_U16((x) >> 0) + +#endif /* _SYS_AUTOMATION_H_ */ diff --git a/sys/sys/kernel.h b/sys/sys/kernel.h --- a/sys/sys/kernel.h +++ b/sys/sys/kernel.h @@ -57,6 +57,8 @@ /* for timestamping SYSINITs; other files may assume this is included here */ #include +#include + /* Global variables for the kernel. */ /* 1.1 */ @@ -225,6 +227,36 @@ const void *udata; /* multiplexer/argument */ }; +/* + * Helper macros for automating sysinits. + * + * The __SI_EXPORT() macro builds a NUL terminated ASCII string, being + * parsed by automation.mk later on. + */ +#define __SI_EXPORT(type, sub, order, var, uniq) \ + _Static_assert((sub) >= 0 && (sub) <= SI_SUB_LAST, "Subsystem is out of range"); \ + _Static_assert((order) >= 0 && (order) <= SI_ORDER_ANY, "Order is out of range"); \ + EXPORT_AUTOMATION(struct { \ + char a[sizeof(#type) - 1]; \ + char b[1]; \ + char c[11]; \ + char d[11]; \ + char e[sizeof(#type "_" #var "_" #uniq)]; \ + } export_##type##_##var, \ + { #type }, \ + { ' ' }, \ + {'0','x',EXPORT_HEX_U32(sub),' '}, \ + {'0','x',EXPORT_HEX_U32(order),' '}, \ + { #type "_" #var "_" #uniq }); \ + extern struct sysinit type##_##var##_##uniq; \ + struct sysinit type##_##var##_##uniq + +#define _SI_EXPORT(...) \ + __SI_EXPORT(__VA_ARGS__) + +#define SI_EXPORT(...) \ + _SI_EXPORT(__VA_ARGS__, AUTOMATION_HINT) + /* * Default: no special processing * @@ -252,51 +284,53 @@ (x->func)(x->data); TSRAW(curthread, TS_EXIT, "SYSINIT", x->name); } -#define C_SYSINIT(uniquifier, subsystem, order, func, ident) \ - static struct sysinit_tslog uniquifier ## _sys_init_tslog = { \ +#define C_SYSINIT(uniq, subsystem, order, func, arg) \ + static const struct sysinit_tslog sysinit_tslog_##uniq = \ + { \ func, \ - (ident), \ - #uniquifier \ + arg, \ + #uniq \ }; \ - static struct sysinit uniquifier ## _sys_init = { \ + SI_EXPORT(sysinit, subsystem, order, uniq) = \ + { \ subsystem, \ order, \ sysinit_tslog_shim, \ - &uniquifier ## _sys_init_tslog \ - }; \ - DATA_WSET(sysinit_set,uniquifier ## _sys_init) + &sysinit_tslog_##uniq, \ + } #else -#define C_SYSINIT(uniquifier, subsystem, order, func, ident) \ - static struct sysinit uniquifier ## _sys_init = { \ +#define C_SYSINIT(uniq, subsystem, order, func, arg) \ + SI_EXPORT(sysinit, subsystem, order, uniq) = \ + { \ subsystem, \ order, \ func, \ - (ident) \ - }; \ - DATA_WSET(sysinit_set,uniquifier ## _sys_init) + arg \ + } #endif -#define SYSINIT(uniquifier, subsystem, order, func, ident) \ +#define SYSINIT(uniquifier, subsystem, order, func, arg) \ C_SYSINIT(uniquifier, subsystem, order, \ - (sysinit_cfunc_t)(sysinit_nfunc_t)func, (void *)(ident)) + (sysinit_cfunc_t)(sysinit_nfunc_t)func, (void *)(arg)) /* * Called on module unload: no special processing */ -#define C_SYSUNINIT(uniquifier, subsystem, order, func, ident) \ - static struct sysinit uniquifier ## _sys_uninit = { \ - subsystem, \ - order, \ +#define C_SYSUNINIT(uniq, subsystem, order, func, arg) \ + SI_EXPORT(sysuninit, subsystem, order, uniq) = \ + { \ + subsystem ^ SI_SUB_LAST, \ + order ^ SI_ORDER_ANY, \ func, \ - (ident) \ - }; \ - DATA_WSET(sysuninit_set,uniquifier ## _sys_uninit) + arg \ + } -#define SYSUNINIT(uniquifier, subsystem, order, func, ident) \ +#define SYSUNINIT(uniquifier, subsystem, order, func, arg) \ C_SYSUNINIT(uniquifier, subsystem, order, \ - (sysinit_cfunc_t)(sysinit_nfunc_t)func, (void *)(ident)) + (sysinit_cfunc_t)(sysinit_nfunc_t)func, (void *)(arg)) void sysinit_add(struct sysinit **set, struct sysinit **set_end); +void sysinit_sort(struct sysinit **set, struct sysinit **set_end); #ifdef _KERNEL