diff --git a/sys/kern/kern_environment.c b/sys/kern/kern_environment.c --- a/sys/kern/kern_environment.c +++ b/sys/kern/kern_environment.c @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -666,6 +667,7 @@ kenvp[i + 1] = NULL; mtx_unlock(&kenv_lock); } + EVENTHANDLER_INVOKE(setenv, name); return (0); } @@ -689,6 +691,7 @@ kenvp[i] = NULL; mtx_unlock(&kenv_lock); zfree(oldenv, M_KENV); + EVENTHANDLER_INVOKE(unsetenv, name); return (0); } mtx_unlock(&kenv_lock); 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 @@ -2351,3 +2351,12 @@ CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, sysctl_kern_function_list, "", "kernel function list"); + +#ifdef VIMAGE +void +linker_file_restore_vnet_variable(linker_file_t lf, void *addr, size_t size) +{ + sx_assert(&kld_sx, SA_LOCKED); + LINKER_RESTORE_VNET_DEFAULT(lf, addr, size); +} +#endif diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c --- a/sys/kern/kern_sysctl.c +++ b/sys/kern/kern_sysctl.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -127,6 +128,7 @@ int recurse); static int sysctl_old_kernel(struct sysctl_req *, const void *, size_t); static int sysctl_new_kernel(struct sysctl_req *, void *, size_t); +static int name2oid(char *, int *, int *, struct sysctl_oid **); static struct sysctl_oid * sysctl_find_oidname(const char *name, struct sysctl_oid_list *list) @@ -512,8 +514,12 @@ if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE && (oidp->oid_kind & CTLFLAG_TUN) != 0 && (oidp->oid_kind & CTLFLAG_NOFETCH) == 0) { - /* only fetch value once */ - oidp->oid_kind |= CTLFLAG_NOFETCH; +#ifdef VIMAGE + /* can fetch multiple times for VNET loader tunable */ + if ((oidp->oid_kind & CTLFLAG_VNET) == 0) +#endif + /* only fetch value once */ + oidp->oid_kind |= CTLFLAG_NOFETCH; /* try to fetch value from kernel environment */ sysctl_load_tunable_by_oid_locked(oidp); } @@ -969,6 +975,129 @@ } SYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_FIRST, sysctl_register_all, NULL); +#ifdef VIMAGE +static void +sysctl_setenv_vnet(void *arg __unused, char *name) +{ + struct sysctl_oid *oidp; + int oid[CTL_MAXNAME]; + int error, nlen; + + SYSCTL_WLOCK(); + error = name2oid(name, oid, &nlen, &oidp); + if (error) + goto out; + + if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE && + (oidp->oid_kind & CTLFLAG_VNET) != 0 && + (oidp->oid_kind & CTLFLAG_TUN) != 0 && + (oidp->oid_kind & CTLFLAG_NOFETCH) == 0) { + /* try to update value from kernel environment */ + sysctl_load_tunable_by_oid_locked(oidp); + } + +out: + SYSCTL_WUNLOCK(); +} + +struct cbargs { + struct sysctl_oid *oidp; + void *addr; + size_t size; +}; + +static int +sysctl_linker_file_cb(linker_file_t lf, void *arg) +{ + struct sysctl_oid **start, **stop, **o; + struct cbargs *args = arg; + + if (linker_file_lookup_set(lf, "sysctl_set", &start, &stop, NULL) != 0) + return (0); + for (o = start; o < stop; o++) { + if (*o == args->oidp) { + linker_file_restore_vnet_variable(lf, args->addr, args->size); + return (1); + } + } + return (0); +} + +static void +sysctl_unsetenv_vnet(void *arg __unused, char *name) +{ + struct rm_priotracker tracker; + struct sysctl_oid *oidp; + int oid[CTL_MAXNAME]; + int error, nlen; + + SYSCTL_RLOCK(&tracker); + error = name2oid(name, oid, &nlen, &oidp); + if (error) + goto out; + + if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE && + (oidp->oid_kind & CTLFLAG_VNET) != 0 && + (oidp->oid_kind & CTLFLAG_TUN) != 0 && + (oidp->oid_kind & CTLFLAG_NOFETCH) == 0) { + void *addr; + size_t size; + + switch (oidp->oid_kind & CTLTYPE) { + case CTLTYPE_INT: + case CTLTYPE_UINT: + size = sizeof(int); + break; + case CTLTYPE_LONG: + case CTLTYPE_ULONG: + size = sizeof(long); + break; + case CTLTYPE_S8: + case CTLTYPE_U8: + size = sizeof(int8_t); + break; + case CTLTYPE_S16: + case CTLTYPE_U16: + size = sizeof(int16_t); + break; + case CTLTYPE_S32: + case CTLTYPE_U32: + size = sizeof(int32_t); + break; + case CTLTYPE_S64: + case CTLTYPE_U64: + size = sizeof(int64_t); + break; + case CTLTYPE_STRING: + MPASS(oidp->oid_arg2 > 0); + size = oidp->oid_arg2; + break; + default: + // not supported + goto out; + } + addr = oidp->oid_arg1; + SYSCTL_RUNLOCK(&tracker); + + struct cbargs args = { + .oidp = oidp, + .addr = addr, + .size = size + }; + linker_file_foreach(sysctl_linker_file_cb, &args); + return; + } +out: + SYSCTL_RUNLOCK(&tracker); +} + +/* + * Register the kernel's setenv / unsetenv events. + */ +EVENTHANDLER_DEFINE(setenv, sysctl_setenv_vnet, NULL, EVENTHANDLER_PRI_ANY); +EVENTHANDLER_DEFINE(unsetenv, sysctl_unsetenv_vnet, NULL, EVENTHANDLER_PRI_ANY); +#endif + /* * "Staff-functions" * diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c --- a/sys/kern/link_elf.c +++ b/sys/kern/link_elf.c @@ -162,6 +162,7 @@ static long link_elf_strtab_get(linker_file_t, caddr_t *); #ifdef VIMAGE static void link_elf_propagate_vnets(linker_file_t); +static void link_elf_restore_vnet_default(linker_file_t, void *, size_t); #endif static int elf_lookup(linker_file_t, Elf_Size, int, Elf_Addr *); @@ -183,6 +184,7 @@ KOBJMETHOD(linker_strtab_get, link_elf_strtab_get), #ifdef VIMAGE KOBJMETHOD(linker_propagate_vnets, link_elf_propagate_vnets), + KOBJMETHOD(linker_restore_vnet_default, link_elf_restore_vnet_default), #endif KOBJMETHOD_END }; @@ -213,6 +215,7 @@ static struct elf_set_head set_pcpu_list; #ifdef VIMAGE static struct elf_set_head set_vnet_list; +static void * linker_kernel_vnet_default; #endif static void @@ -506,6 +509,8 @@ TAILQ_INIT(&set_pcpu_list); #ifdef VIMAGE TAILQ_INIT(&set_vnet_list); + linker_kernel_vnet_default = malloc(VNET_STOP - VNET_START, M_LINKER, M_WAITOK); + memcpy(linker_kernel_vnet_default, (void *)VNET_START, VNET_STOP - VNET_START); #endif } @@ -1941,6 +1946,25 @@ vnet_data_copy((void *)ef->vnet_base, size); } } + +static void +link_elf_restore_vnet_default(linker_file_t lf, void *addr, size_t size) +{ + elf_file_t ef = (elf_file_t)lf; + uintptr_t *oaddr; + + MPASS(size > 0); + if (ef->vnet_base == 0) { + MPASS(lf == linker_kernel_file); + MPASS(VNET_START <= (uintptr_t)addr && (uintptr_t)addr + size <= VNET_STOP); + oaddr = (uintptr_t)linker_kernel_vnet_default + ((uintptr_t)addr - VNET_START); + } else { + MPASS((uintptr_t)ef->vnet_base <= (uintptr_t)addr && + (uintptr_t)addr + size <= (uintptr_t)ef->vnet_base + (ef->vnet_stop - ef->vnet_start)); + oaddr = (uintptr_t)ef->vnet_start + ((uintptr_t)addr - (uintptr_t)ef->vnet_base); + } + memcpy(addr, (void *)oaddr, size); +} #endif #if defined(__i386__) || defined(__amd64__) || defined(__aarch64__) || defined(__powerpc__) diff --git a/sys/kern/link_elf_obj.c b/sys/kern/link_elf_obj.c --- a/sys/kern/link_elf_obj.c +++ b/sys/kern/link_elf_obj.c @@ -75,6 +75,9 @@ int flags; /* Section flags. */ int sec; /* Original section number. */ char *name; +#ifdef VIMAGE + void *oaddr; /* Original addr, for vnet */ +#endif } Elf_progent; typedef struct { @@ -152,6 +155,7 @@ static long link_elf_strtab_get(linker_file_t, caddr_t *); #ifdef VIMAGE static void link_elf_propagate_vnets(linker_file_t); +static void link_elf_restore_vnet_default(linker_file_t, void *, size_t); #endif static int elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps, @@ -175,6 +179,7 @@ KOBJMETHOD(linker_strtab_get, link_elf_strtab_get), #ifdef VIMAGE KOBJMETHOD(linker_propagate_vnets, link_elf_propagate_vnets), + KOBJMETHOD(linker_restore_vnet_default, link_elf_restore_vnet_default), #endif KOBJMETHOD_END }; @@ -546,6 +551,7 @@ } memcpy(vnet_data, ef->progtab[pb].addr, ef->progtab[pb].size); + ef->progtab[pb].oaddr = ef->progtab[pb].addr; ef->progtab[pb].addr = vnet_data; #endif } else if ((ef->progtab[pb].name != NULL && @@ -1070,29 +1076,14 @@ "for %s\n", __func__, (uintmax_t)shdr[i].sh_size, filename); + error = ENOSPC; + goto out; } } -#ifdef VIMAGE - else if (ef->progtab[pb].name != NULL && - !strcmp(ef->progtab[pb].name, VNET_SETNAME)) { - ef->progtab[pb].addr = - vnet_data_alloc(shdr[i].sh_size); - if (ef->progtab[pb].addr == NULL) { - printf("%s: vnet module space is out " - "of space; cannot allocate %#jx " - "for %s\n", __func__, - (uintmax_t)shdr[i].sh_size, - filename); - } - } -#endif else ef->progtab[pb].addr = (void *)(uintptr_t)mapbase; - if (ef->progtab[pb].addr == NULL) { - error = ENOSPC; - goto out; - } + ef->progtab[pb].size = shdr[i].sh_size; ef->progtab[pb].flags = shdr[i].sh_flags; ef->progtab[pb].sec = i; @@ -1120,6 +1111,29 @@ } else bzero(ef->progtab[pb].addr, shdr[i].sh_size); +#ifdef VIMAGE + /* Initialize the vnet area. */ + if (ef->progtab[pb].name != NULL && + !strcmp(ef->progtab[pb].name, VNET_SETNAME)) { + void *vnet_data; + + vnet_data = vnet_data_alloc(shdr[i].sh_size); + if (vnet_data == NULL) { + printf("%s: vnet module space is out " + "of space; cannot allocate %#jx " + "for %s\n", __func__, + (uintmax_t)shdr[i].sh_size, + filename); + error = ENOSPC; + goto out; + } + memcpy(vnet_data, ef->progtab[pb].addr, + ef->progtab[pb].size); + vnet_data_copy(vnet_data, shdr[i].sh_size); + ef->progtab[pb].oaddr = ef->progtab[pb].addr; + ef->progtab[pb].addr = vnet_data; + } +#endif /* Update all symbol values with the offset. */ for (j = 0; j < ef->ddbsymcnt; j++) { es = &ef->ddbsymtab[j]; @@ -1878,4 +1892,32 @@ } } } + +static void +link_elf_restore_vnet_default(linker_file_t lf, void *addr, size_t size) +{ + elf_file_t ef = (elf_file_t)lf; + + MPASS(size > 0); + MPASS(ef->progtab != NULL); + + for (int i = 0; i < ef->nprogtab; i++) { + if (ef->progtab[i].size == 0) + continue; + if (ef->progtab[i].name == NULL) + continue; + if (strcmp(ef->progtab[i].name, VNET_SETNAME) != 0) + continue; + + MPASS(ef->progtab[i].oaddr != NULL); + MPASS(ef->progtab[i].addr <= addr && + ((uintptr_t)addr + size) <= ((uintptr_t)ef->progtab[i].addr + + ef->progtab[i].size)); + + memcpy(addr, (void *)((uintptr_t)ef->progtab[i].oaddr + + ((uintptr_t)addr - (uintptr_t)ef->progtab[i].addr)), + size); + break; + } +} #endif diff --git a/sys/kern/linker_if.m b/sys/kern/linker_if.m --- a/sys/kern/linker_if.m +++ b/sys/kern/linker_if.m @@ -162,4 +162,13 @@ METHOD void propagate_vnets { linker_file_t file; }; + +# +# Restore vnets' default values +# +METHOD void restore_vnet_default { + linker_file_t file; + void * addr; + size_t size; +}; #endif diff --git a/sys/sys/eventhandler.h b/sys/sys/eventhandler.h --- a/sys/sys/eventhandler.h +++ b/sys/sys/eventhandler.h @@ -326,4 +326,9 @@ typedef void (*rt_addrmsg_fn)(void *, struct ifaddr *, int); EVENTHANDLER_DECLARE(rt_addrmsg, rt_addrmsg_fn); +/* Kernel environment variable change event */ +typedef void (*env_change_fn)(void *, const char *); +EVENTHANDLER_DECLARE(setenv, env_change_fn); +EVENTHANDLER_DECLARE(unsetenv, env_change_fn); + #endif /* _SYS_EVENTHANDLER_H_ */ diff --git a/sys/sys/linker.h b/sys/sys/linker.h --- a/sys/sys/linker.h +++ b/sys/sys/linker.h @@ -205,6 +205,10 @@ int linker_kldload_busy(int flags); void linker_kldload_unbusy(int flags); +#ifdef VIMAGE +void linker_file_restore_vnet_variable(linker_file_t lf, void *addr, + size_t size); +#endif #endif /* _KERNEL */ /*