diff options
Diffstat (limited to 'sys/compat')
88 files changed, 3897 insertions, 1178 deletions
diff --git a/sys/compat/freebsd32/freebsd32.h b/sys/compat/freebsd32/freebsd32.h index 9d724c93fee7..1436b630689f 100644 --- a/sys/compat/freebsd32/freebsd32.h +++ b/sys/compat/freebsd32/freebsd32.h @@ -30,20 +30,14 @@ #define _COMPAT_FREEBSD32_FREEBSD32_H_ #include <sys/abi_compat.h> +#include <sys/devicestat.h> +#include <sys/event.h> +#include <sys/mount.h> #include <sys/procfs.h> #include <sys/socket.h> #include <sys/user.h> #include <sys/_ffcounter.h> -/* - * i386 is the only arch with a 32-bit time_t - */ -#ifdef __amd64__ -typedef int32_t time32_t; -#else -typedef int64_t time32_t; -#endif - struct timeval32 { time32_t tv_sec; int32_t tv_usec; @@ -61,26 +55,24 @@ struct itimerspec32 { struct bintime32 { time32_t sec; - uint32_t frac[2]; + freebsd32_uint64_t frac; }; struct ffclock_estimate32 { struct bintime32 update_time; - ffcounter update_ffcount; - ffcounter leapsec_next; - uint64_t period; + freebsd32_uint64_t update_ffcount; + freebsd32_uint64_t leapsec_next; + freebsd32_uint64_t period; uint32_t errb_abs; uint32_t errb_rate; uint32_t status; int16_t leapsec_total; int8_t leapsec; int8_t _pad; -} -#if defined(__amd64__) -__attribute__((packed)) -#endif -; -#if defined(__amd64__) +}; +_Static_assert(sizeof(ffcounter) == sizeof(freebsd32_uint64_t), + "'ffcounter' size discrepancy'"); +#if defined(__amd64__) || defined(__i386__) _Static_assert(sizeof(struct ffclock_estimate32) == 52, "ffclock_estimate32 size"); #else _Static_assert(sizeof(struct ffclock_estimate32) == 56, "ffclock_estimate32 size"); @@ -236,12 +228,12 @@ struct stat32 { #endif struct timespec32 st_birthtim; off_t st_size; - int64_t st_blocks; + freebsd32_uint64_t st_blocks; uint32_t st_blksize; uint32_t st_flags; - uint64_t st_gen; - uint64_t st_filerev; - uint64_t st_spare[9]; + freebsd32_uint64_t st_gen; + freebsd32_uint64_t st_filerev; + freebsd32_uint64_t st_spare[9]; }; struct freebsd11_stat32 { uint32_t st_dev; @@ -255,7 +247,7 @@ struct freebsd11_stat32 { struct timespec32 st_mtim; struct timespec32 st_ctim; off_t st_size; - int64_t st_blocks; + freebsd32_uint64_t st_blocks; uint32_t st_blksize; uint32_t st_flags; uint32_t st_gen; @@ -380,7 +372,7 @@ struct kinfo_proc32 { u_int ki_slptime; u_int ki_swtime; u_int ki_cow; - uint64_t ki_runtime; + freebsd32_uint64_t ki_runtime; struct timeval32 ki_start; struct timeval32 ki_childtime; int ki_flag; @@ -402,7 +394,7 @@ struct kinfo_proc32 { char ki_moretdname[MAXCOMLEN-TDNAMLEN+1]; char ki_sparestrings[46]; int ki_spareints[KI_NSPARE_INT]; - uint64_t ki_tdev; + freebsd32_uint64_t ki_tdev; int ki_oncpu; int ki_lastcpu; int ki_tracer; @@ -419,6 +411,7 @@ struct kinfo_proc32 { uint32_t ki_kstack; uint32_t ki_udata; uint32_t ki_tdaddr; + uint32_t ki_pd; uint32_t ki_uerrmsg; uint32_t ki_spareptrs[KI_NSPARE_PTR]; /* spare room for growth */ int ki_sparelongs[KI_NSPARE_LONG]; @@ -457,12 +450,12 @@ struct kinfo_knote32 { union { struct { int knt_vnode_type; - uint32_t knt_vnode_fsid[2]; - uint32_t knt_vnode_fileid[2]; + freebsd32_uint64_t knt_vnode_fsid; + freebsd32_uint64_t knt_vnode_fileid; char knt_vnode_fullpath[PATH_MAX]; } knt_vnode; struct { - uint32_t knt_pipe_ino[2]; + freebsd32_uint64_t knt_pipe_ino; } knt_pipe; }; }; @@ -531,4 +524,28 @@ struct ptrace_sc_remote32 { uint32_t pscr_args; }; +struct devstat32 { + u_int sequence0; + int allocated; + u_int start_count; + u_int end_count; + struct bintime32 busy_from; + struct { u_int32_t stqe_next; } dev_links; + u_int32_t device_number; + char device_name[DEVSTAT_NAME_LEN]; + int unit_number; + freebsd32_uint64_t bytes[DEVSTAT_N_TRANS_FLAGS]; + freebsd32_uint64_t operations[DEVSTAT_N_TRANS_FLAGS]; + struct bintime32 duration[DEVSTAT_N_TRANS_FLAGS]; + struct bintime32 busy_time; + struct bintime32 creation_time; + u_int32_t block_size; + freebsd32_uint64_t tag_types[3]; + devstat_support_flags flags; + devstat_type_flags device_type; + devstat_priority priority; + u_int32_t id; + u_int sequence1; +}; + #endif /* !_COMPAT_FREEBSD32_FREEBSD32_H_ */ diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index 7913940338c2..4ec6dd452b32 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -203,6 +203,7 @@ void freebsd32_rusage_out(const struct rusage *s, struct rusage32 *s32) { + bzero(s32, sizeof(*s32)); TV_CP(*s, *s32, ru_utime); TV_CP(*s, *s32, ru_stime); CP(*s, *s32, ru_maxrss); @@ -280,6 +281,37 @@ freebsd32_wait6(struct thread *td, struct freebsd32_wait6_args *uap) return (error); } +int +freebsd32_pdwait(struct thread *td, struct freebsd32_pdwait_args *uap) +{ + struct __wrusage32 wru32; + struct __wrusage wru, *wrup; + struct __siginfo32 si32; + struct __siginfo si, *sip; + int error, status; + + wrup = uap->wrusage != NULL ? &wru : NULL; + if (uap->info != NULL) { + sip = &si; + bzero(sip, sizeof(*sip)); + } else { + sip = NULL; + } + error = kern_pdwait(td, uap->fd, &status, uap->options, wrup, sip); + if (uap->status != NULL && error == 0) + error = copyout(&status, uap->status, sizeof(status)); + if (uap->wrusage != NULL && error == 0) { + freebsd32_rusage_out(&wru.wru_self, &wru32.wru_self); + freebsd32_rusage_out(&wru.wru_children, &wru32.wru_children); + error = copyout(&wru32, uap->wrusage, sizeof(wru32)); + } + if (uap->info != NULL && error == 0) { + siginfo_to_siginfo32 (&si, &si32); + error = copyout(&si32, uap->info, sizeof(si32)); + } + return (error); +} + #ifdef COMPAT_FREEBSD4 static void copy_statfs(struct statfs *in, struct ostatfs32 *out) @@ -681,31 +713,16 @@ freebsd32_pselect(struct thread *td, struct freebsd32_pselect_args *uap) static void freebsd32_kevent_to_kevent32(const struct kevent *kevp, struct kevent32 *ks32) { - uint64_t e; int j; CP(*kevp, *ks32, ident); CP(*kevp, *ks32, filter); CP(*kevp, *ks32, flags); CP(*kevp, *ks32, fflags); -#if BYTE_ORDER == LITTLE_ENDIAN - ks32->data1 = kevp->data; - ks32->data2 = kevp->data >> 32; -#else - ks32->data1 = kevp->data >> 32; - ks32->data2 = kevp->data; -#endif + FU64_CP(*kevp, *ks32, data); PTROUT_CP(*kevp, *ks32, udata); - for (j = 0; j < nitems(kevp->ext); j++) { - e = kevp->ext[j]; -#if BYTE_ORDER == LITTLE_ENDIAN - ks32->ext64[2 * j] = e; - ks32->ext64[2 * j + 1] = e >> 32; -#else - ks32->ext64[2 * j] = e >> 32; - ks32->ext64[2 * j + 1] = e; -#endif - } + for (j = 0; j < nitems(kevp->ext); j++) + FU64_CP(*kevp, *ks32, ext[j]); } void @@ -722,38 +739,13 @@ freebsd32_kinfo_knote_to_32(const struct kinfo_knote *kin, break; case KNOTE_EXTDATA_VNODE: CP(*kin, *kin32, knt_vnode.knt_vnode_type); -#if BYTE_ORDER == LITTLE_ENDIAN - kin32->knt_vnode.knt_vnode_fsid[0] = kin->knt_vnode. - knt_vnode_fsid; - kin32->knt_vnode.knt_vnode_fsid[1] = kin->knt_vnode. - knt_vnode_fsid >> 32; - kin32->knt_vnode.knt_vnode_fileid[0] = kin->knt_vnode. - knt_vnode_fileid; - kin32->knt_vnode.knt_vnode_fileid[1] = kin->knt_vnode. - knt_vnode_fileid >> 32; -#else - kin32->knt_vnode.knt_vnode_fsid[1] = kin->knt_vnode. - knt_vnode_fsid; - kin32->knt_vnode.knt_vnode_fsid[0] = kin->knt_vnode. - knt_vnode_fsid >> 32; - kin32->knt_vnode.knt_vnode_fileid[1] = kin->knt_vnode. - knt_vnode_fileid; - kin32->knt_vnode.knt_vnode_fileid[0] = kin->knt_vnode. - knt_vnode_fileid >> 32; -#endif + FU64_CP(*kin, *kin32, knt_vnode.knt_vnode_fsid); + FU64_CP(*kin, *kin32, knt_vnode.knt_vnode_fileid); memcpy(kin32->knt_vnode.knt_vnode_fullpath, kin->knt_vnode.knt_vnode_fullpath, PATH_MAX); break; case KNOTE_EXTDATA_PIPE: -#if BYTE_ORDER == LITTLE_ENDIAN - kin32->knt_pipe.knt_pipe_ino[0] = kin->knt_pipe.knt_pipe_ino; - kin32->knt_pipe.knt_pipe_ino[1] = kin->knt_pipe. - knt_pipe_ino >> 32; -#else - kin32->knt_pipe.knt_pipe_ino[1] = kin->knt_pipe.knt_pipe_ino; - kin32->knt_pipe.knt_pipe_ino[0] = kin->knt_pipe. - knt_pipe_ino >> 32; -#endif + FU64_CP(*kin, *kin32, knt_pipe.knt_pipe_ino); break; } } @@ -765,7 +757,7 @@ static int freebsd32_kevent_copyout(void *arg, struct kevent *kevp, int count) { struct freebsd32_kevent_args *uap; - struct kevent32 ks32[KQ_NEVENTS]; + struct kevent32 ks32[KQ_NEVENTS] = {}; int i, error; KASSERT(count <= KQ_NEVENTS, ("count (%d) > KQ_NEVENTS", count)); @@ -787,7 +779,6 @@ freebsd32_kevent_copyin(void *arg, struct kevent *kevp, int count) { struct freebsd32_kevent_args *uap; struct kevent32 ks32[KQ_NEVENTS]; - uint64_t e; int i, j, error; KASSERT(count <= KQ_NEVENTS, ("count (%d) > KQ_NEVENTS", count)); @@ -803,20 +794,10 @@ freebsd32_kevent_copyin(void *arg, struct kevent *kevp, int count) CP(ks32[i], kevp[i], filter); CP(ks32[i], kevp[i], flags); CP(ks32[i], kevp[i], fflags); - kevp[i].data = PAIR32TO64(uint64_t, ks32[i].data); + FU64_CP(ks32[i], kevp[i], data); PTRIN_CP(ks32[i], kevp[i], udata); - for (j = 0; j < nitems(kevp->ext); j++) { -#if BYTE_ORDER == LITTLE_ENDIAN - e = ks32[i].ext64[2 * j + 1]; - e <<= 32; - e += ks32[i].ext64[2 * j]; -#else - e = ks32[i].ext64[2 * j]; - e <<= 32; - e += ks32[i].ext64[2 * j + 1]; -#endif - kevp[i].ext[j] = e; - } + for (j = 0; j < nitems(kevp->ext); j++) + FU64_CP(ks32[i], kevp[i], ext[j]); } done: return (error); @@ -2306,11 +2287,11 @@ copy_stat(struct stat *in, struct stat32 *out) TS_CP(*in, *out, st_mtim); TS_CP(*in, *out, st_ctim); CP(*in, *out, st_size); - CP(*in, *out, st_blocks); + FU64_CP(*in, *out, st_blocks); CP(*in, *out, st_blksize); CP(*in, *out, st_flags); - CP(*in, *out, st_gen); - CP(*in, *out, st_filerev); + FU64_CP(*in, *out, st_gen); + FU64_CP(*in, *out, st_filerev); CP(*in, *out, st_bsdflags); TS_CP(*in, *out, st_birthtim); out->st_padding1 = 0; @@ -2519,7 +2500,7 @@ freebsd11_cvtstat32(struct stat *in, struct freebsd11_stat32 *out) TS_CP(*in, *out, st_mtim); TS_CP(*in, *out, st_ctim); CP(*in, *out, st_size); - CP(*in, *out, st_blocks); + FU64_CP(*in, *out, st_blocks); CP(*in, *out, st_blksize); CP(*in, *out, st_flags); CP(*in, *out, st_gen); @@ -4166,9 +4147,9 @@ freebsd32_ffclock_setestimate(struct thread *td, CP(cest.update_time, cest32.update_time, sec); memcpy(&cest.update_time.frac, &cest32.update_time.frac, sizeof(uint64_t)); - CP(cest, cest32, update_ffcount); - CP(cest, cest32, leapsec_next); - CP(cest, cest32, period); + FU64_CP(cest, cest32, update_ffcount); + FU64_CP(cest, cest32, leapsec_next); + FU64_CP(cest, cest32, period); CP(cest, cest32, errb_abs); CP(cest, cest32, errb_rate); CP(cest, cest32, status); @@ -4196,9 +4177,9 @@ freebsd32_ffclock_getestimate(struct thread *td, CP(cest32.update_time, cest.update_time, sec); memcpy(&cest32.update_time.frac, &cest.update_time.frac, sizeof(uint64_t)); - CP(cest32, cest, update_ffcount); - CP(cest32, cest, leapsec_next); - CP(cest32, cest, period); + FU64_CP(cest32, cest, update_ffcount); + FU64_CP(cest32, cest, leapsec_next); + FU64_CP(cest32, cest, period); CP(cest32, cest, errb_abs); CP(cest32, cest, errb_rate); CP(cest32, cest, status); diff --git a/sys/compat/freebsd32/freebsd32_proto.h b/sys/compat/freebsd32/freebsd32_proto.h index 5c0efc64f8a7..12458ed4cc4d 100644 --- a/sys/compat/freebsd32/freebsd32_proto.h +++ b/sys/compat/freebsd32/freebsd32_proto.h @@ -699,6 +699,13 @@ struct freebsd32_setcred_args { char wcred_l_[PADL_(const struct setcred32 *)]; const struct setcred32 * wcred; char wcred_r_[PADR_(const struct setcred32 *)]; char size_l_[PADL_(size_t)]; size_t size; char size_r_[PADR_(size_t)]; }; +struct freebsd32_pdwait_args { + char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)]; + char status_l_[PADL_(int *)]; int * status; char status_r_[PADR_(int *)]; + char options_l_[PADL_(int)]; int options; char options_r_[PADR_(int)]; + char wrusage_l_[PADL_(struct __wrusage32 *)]; struct __wrusage32 * wrusage; char wrusage_r_[PADR_(struct __wrusage32 *)]; + char info_l_[PADL_(struct __siginfo32 *)]; struct __siginfo32 * info; char info_r_[PADR_(struct __siginfo32 *)]; +}; int freebsd32_wait4(struct thread *, struct freebsd32_wait4_args *); int freebsd32_ptrace(struct thread *, struct freebsd32_ptrace_args *); int freebsd32_recvmsg(struct thread *, struct freebsd32_recvmsg_args *); @@ -817,6 +824,7 @@ int freebsd32_aio_readv(struct thread *, struct freebsd32_aio_readv_args *); int freebsd32_timerfd_gettime(struct thread *, struct freebsd32_timerfd_gettime_args *); int freebsd32_timerfd_settime(struct thread *, struct freebsd32_timerfd_settime_args *); int freebsd32_setcred(struct thread *, struct freebsd32_setcred_args *); +int freebsd32_pdwait(struct thread *, struct freebsd32_pdwait_args *); #ifdef COMPAT_43 @@ -1319,6 +1327,7 @@ int freebsd11_freebsd32_fstatat(struct thread *, struct freebsd11_freebsd32_fsta #define FREEBSD32_SYS_AUE_freebsd32_timerfd_gettime AUE_TIMERFD #define FREEBSD32_SYS_AUE_freebsd32_timerfd_settime AUE_TIMERFD #define FREEBSD32_SYS_AUE_freebsd32_setcred AUE_SETCRED +#define FREEBSD32_SYS_AUE_freebsd32_pdwait AUE_PDWAIT #undef PAD_ #undef PADL_ diff --git a/sys/compat/freebsd32/freebsd32_syscall.h b/sys/compat/freebsd32/freebsd32_syscall.h index f8ef7e4a20d3..9fed7ec58669 100644 --- a/sys/compat/freebsd32/freebsd32_syscall.h +++ b/sys/compat/freebsd32/freebsd32_syscall.h @@ -517,4 +517,7 @@ #define FREEBSD32_SYS_setgroups 596 #define FREEBSD32_SYS_jail_attach_jd 597 #define FREEBSD32_SYS_jail_remove_jd 598 -#define FREEBSD32_SYS_MAXSYSCALL 600 +#define FREEBSD32_SYS_pdrfork 600 +#define FREEBSD32_SYS_freebsd32_pdwait 601 +#define FREEBSD32_SYS_renameat2 602 +#define FREEBSD32_SYS_MAXSYSCALL 603 diff --git a/sys/compat/freebsd32/freebsd32_syscalls.c b/sys/compat/freebsd32/freebsd32_syscalls.c index 645cdccbc02d..d91d45130c89 100644 --- a/sys/compat/freebsd32/freebsd32_syscalls.c +++ b/sys/compat/freebsd32/freebsd32_syscalls.c @@ -605,4 +605,7 @@ const char *freebsd32_syscallnames[] = { "jail_attach_jd", /* 597 = jail_attach_jd */ "jail_remove_jd", /* 598 = jail_remove_jd */ "#599", /* 599 = kexec_load */ + "pdrfork", /* 600 = pdrfork */ + "freebsd32_pdwait", /* 601 = freebsd32_pdwait */ + "renameat2", /* 602 = renameat2 */ }; diff --git a/sys/compat/freebsd32/freebsd32_sysent.c b/sys/compat/freebsd32/freebsd32_sysent.c index 240b54ae9011..68c0388eaa79 100644 --- a/sys/compat/freebsd32/freebsd32_sysent.c +++ b/sys/compat/freebsd32/freebsd32_sysent.c @@ -667,4 +667,7 @@ struct sysent freebsd32_sysent[] = { { .sy_narg = AS(jail_attach_jd_args), .sy_call = (sy_call_t *)sys_jail_attach_jd, .sy_auevent = AUE_JAIL_ATTACH, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 597 = jail_attach_jd */ { .sy_narg = AS(jail_remove_jd_args), .sy_call = (sy_call_t *)sys_jail_remove_jd, .sy_auevent = AUE_JAIL_REMOVE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 598 = jail_remove_jd */ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 599 = freebsd32_kexec_load */ + { .sy_narg = AS(pdrfork_args), .sy_call = (sy_call_t *)sys_pdrfork, .sy_auevent = AUE_PDRFORK, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 600 = pdrfork */ + { .sy_narg = AS(freebsd32_pdwait_args), .sy_call = (sy_call_t *)freebsd32_pdwait, .sy_auevent = AUE_PDWAIT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 601 = freebsd32_pdwait */ + { .sy_narg = AS(renameat2_args), .sy_call = (sy_call_t *)sys_renameat2, .sy_auevent = AUE_RENAMEAT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 602 = renameat2 */ }; diff --git a/sys/compat/freebsd32/freebsd32_systrace_args.c b/sys/compat/freebsd32/freebsd32_systrace_args.c index 29a5497e9efa..e85cf4765c98 100644 --- a/sys/compat/freebsd32/freebsd32_systrace_args.c +++ b/sys/compat/freebsd32/freebsd32_systrace_args.c @@ -3427,6 +3427,37 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) *n_args = 1; break; } + /* pdrfork */ + case 600: { + struct pdrfork_args *p = params; + uarg[a++] = (intptr_t)p->fdp; /* int * */ + iarg[a++] = p->pdflags; /* int */ + iarg[a++] = p->rfflags; /* int */ + *n_args = 3; + break; + } + /* freebsd32_pdwait */ + case 601: { + struct freebsd32_pdwait_args *p = params; + iarg[a++] = p->fd; /* int */ + uarg[a++] = (intptr_t)p->status; /* int * */ + iarg[a++] = p->options; /* int */ + uarg[a++] = (intptr_t)p->wrusage; /* struct __wrusage32 * */ + uarg[a++] = (intptr_t)p->info; /* struct __siginfo32 * */ + *n_args = 5; + break; + } + /* renameat2 */ + case 602: { + struct renameat2_args *p = params; + iarg[a++] = p->oldfd; /* int */ + uarg[a++] = (intptr_t)p->old; /* const char * */ + iarg[a++] = p->newfd; /* int */ + uarg[a++] = (intptr_t)p->new; /* const char * */ + iarg[a++] = p->flags; /* int */ + *n_args = 5; + break; + } default: *n_args = 0; break; @@ -9256,6 +9287,66 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) break; }; break; + /* pdrfork */ + case 600: + switch (ndx) { + case 0: + p = "userland int *"; + break; + case 1: + p = "int"; + break; + case 2: + p = "int"; + break; + default: + break; + }; + break; + /* freebsd32_pdwait */ + case 601: + switch (ndx) { + case 0: + p = "int"; + break; + case 1: + p = "userland int *"; + break; + case 2: + p = "int"; + break; + case 3: + p = "userland struct __wrusage32 *"; + break; + case 4: + p = "userland struct __siginfo32 *"; + break; + default: + break; + }; + break; + /* renameat2 */ + case 602: + switch (ndx) { + case 0: + p = "int"; + break; + case 1: + p = "userland const char *"; + break; + case 2: + p = "int"; + break; + case 3: + p = "userland const char *"; + break; + case 4: + p = "int"; + break; + default: + break; + }; + break; default: break; }; @@ -11174,6 +11265,21 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) if (ndx == 0 || ndx == 1) p = "int"; break; + /* pdrfork */ + case 600: + if (ndx == 0 || ndx == 1) + p = "int"; + break; + /* freebsd32_pdwait */ + case 601: + if (ndx == 0 || ndx == 1) + p = "int"; + break; + /* renameat2 */ + case 602: + if (ndx == 0 || ndx == 1) + p = "int"; + break; default: break; }; diff --git a/sys/compat/lindebugfs/lindebugfs.c b/sys/compat/lindebugfs/lindebugfs.c index 8cddc6f390bc..88b92afd374a 100644 --- a/sys/compat/lindebugfs/lindebugfs.c +++ b/sys/compat/lindebugfs/lindebugfs.c @@ -117,9 +117,14 @@ debugfs_fill(PFS_FILL_ARGS) struct dentry_meta *d; struct linux_file lf = {}; struct vnode vn; - char *buf; - int rc; - off_t off = 0; + struct iovec *iov; + size_t cnt, orig_resid; + ssize_t rc; + off_t off; + + /* Linux file operations assume a pointer to a user buffer. */ + if (uio->uio_segflg != UIO_USERSPACE) + return (EOPNOTSUPP); if ((rc = linux_set_current_flags(curthread, M_NOWAIT))) return (rc); @@ -130,42 +135,64 @@ debugfs_fill(PFS_FILL_ARGS) rc = d->dm_fops->open(&vn, &lf); if (rc < 0) { #ifdef INVARIANTS - printf("%s:%d open failed with %d\n", __func__, __LINE__, rc); + printf("%s:%d open failed with %zd\n", __func__, __LINE__, rc); #endif return (-rc); } - rc = -ENODEV; - switch (uio->uio_rw) { - case UIO_READ: - if (d->dm_fops->read != NULL) { - rc = -ENOMEM; - buf = malloc(sb->s_size, M_DFSINT, M_ZERO | M_NOWAIT); - if (buf != NULL) { - rc = d->dm_fops->read(&lf, buf, sb->s_size, - &off); - if (rc > 0) - sbuf_bcpy(sb, buf, strlen(buf)); - - free(buf, M_DFSINT); - } + off = uio->uio_offset; + orig_resid = uio->uio_resid; + while (uio->uio_resid > 0) { + KASSERT(uio->uio_iovcnt > 0, + ("%s: uio %p iovcnt underflow", __func__, uio)); + + iov = uio->uio_iov; + cnt = iov->iov_len; + if (cnt == 0) { + uio->uio_iov++; + uio->uio_iovcnt--; + continue; } - break; - case UIO_WRITE: - if (d->dm_fops->write != NULL) { - sbuf_finish(sb); - rc = d->dm_fops->write(&lf, sbuf_data(sb), sbuf_len(sb), - &off); + if (cnt > uio->uio_resid) + cnt = uio->uio_resid; + + switch (uio->uio_rw) { + case UIO_READ: + if (d->dm_fops->read != NULL) + rc = d->dm_fops->read(&lf, iov->iov_base, cnt, + &off); + else + rc = -ENODEV; + break; + case UIO_WRITE: + if (d->dm_fops->write != NULL) + rc = d->dm_fops->write(&lf, iov->iov_base, cnt, + &off); + else + rc = -ENODEV; + break; } - break; + + if (rc <= 0) + break; + + iov->iov_base = (char *)iov->iov_base + rc; + iov->iov_len -= rc; + uio->uio_resid -= rc; + uio->uio_offset = off; } if (d->dm_fops->release) d->dm_fops->release(&vn, &lf); + /* Return success for short operations. */ + if (orig_resid != uio->uio_resid) + rc = 0; + if (rc < 0) { #ifdef INVARIANTS - printf("%s:%d read/write failed with %d\n", __func__, __LINE__, rc); + printf("%s:%d read/write failed with %zd\n", __func__, __LINE__, + rc); #endif return (-rc); } @@ -207,7 +234,7 @@ debugfs_create_file(const char *name, umode_t mode, flags = fops->write ? PFS_RDWR : PFS_RD; pfs_create_file(pnode, &dnode->d_pfs_node, name, debugfs_fill, - debugfs_attr, NULL, debugfs_destroy, flags | PFS_NOWAIT); + debugfs_attr, NULL, debugfs_destroy, flags | PFS_RAW | PFS_NOWAIT); if (dnode->d_pfs_node == NULL) { free(dm, M_DFSINT); return (NULL); @@ -272,6 +299,9 @@ debugfs_create_dir(const char *name, struct dentry *parent) struct dentry *dnode; struct pfs_node *pnode; + if (name == NULL) + return (NULL); + dm = malloc(sizeof(*dm), M_DFSINT, M_NOWAIT | M_ZERO); if (dm == NULL) return (NULL); @@ -668,7 +698,7 @@ fops_str_read(struct file *filp, char __user *ubuf, size_t read_size, } static ssize_t -fops_str_write(struct file *filp, const char *buf, size_t write_size, +fops_str_write(struct file *filp, const char __user *buf, size_t write_size, loff_t *ppos) { char *old, *new; diff --git a/sys/compat/linux/linux_file.c b/sys/compat/linux/linux_file.c index daddafa325ad..30e79a53ad2a 100644 --- a/sys/compat/linux/linux_file.c +++ b/sys/compat/linux/linux_file.c @@ -398,12 +398,50 @@ struct l_dirent64 { roundup(offsetof(struct l_dirent64, d_name) + (namlen) + 1, \ sizeof(uint64_t)) +/* + * Do kern_getdirentries() and then skip over any invalid entries. + * (Repeat, if there are no valid entries.) + * Adjust bufp and lenp. + */ +static int +linux_getdirentries(struct thread *td, int fd, caddr_t *bufp, int buflen, + off_t *basep, int *lenp) +{ + struct dirent *bdp; + caddr_t buf; + int error, len; + + /* Loop around until a valid entry is found or at EOF. */ + for (;;) { + error = kern_getdirentries(td, fd, *bufp, buflen, + basep, NULL, UIO_SYSSPACE); + if (error != 0) + return (error); + len = td->td_retval[0]; + if (len == 0) { + *lenp = 0; + return (0); + } + buf = *bufp; + while (len > 0) { + bdp = (struct dirent *)buf; + if (bdp->d_fileno != 0) { + *bufp = buf; + *lenp = len; + return (0); + } + buf += bdp->d_reclen; + len -= bdp->d_reclen; + } + } +} + #ifdef LINUX_LEGACY_SYSCALLS int linux_getdents(struct thread *td, struct linux_getdents_args *args) { struct dirent *bdp; - caddr_t inp, buf; /* BSD-format */ + caddr_t inp, buf, bufsav; /* BSD-format */ int len, reclen; /* BSD-format */ caddr_t outp; /* Linux-format */ int resid, linuxreclen; /* Linux-format */ @@ -413,11 +451,11 @@ linux_getdents(struct thread *td, struct linux_getdents_args *args) int buflen, error; size_t retval; - buflen = min(args->count, MAXBSIZE); - buf = malloc(buflen, M_LINUX, M_WAITOK); + buflen = min(roundup2(args->count, DEV_BSIZE), MAXBSIZE); + bufsav = buf = malloc(buflen, M_LINUX, M_WAITOK); - error = kern_getdirentries(td, args->fd, buf, buflen, - &base, NULL, UIO_SYSSPACE); + error = linux_getdirentries(td, args->fd, &buf, buflen, + &base, &len); if (error != 0) { error = linux_getdents_error(td, args->fd, error); goto out1; @@ -425,7 +463,6 @@ linux_getdents(struct thread *td, struct linux_getdents_args *args) lbuf = malloc(LINUX_RECLEN(LINUX_NAME_MAX), M_LINUX, M_WAITOK | M_ZERO); - len = td->td_retval[0]; inp = buf; outp = (caddr_t)args->dent; resid = args->count; @@ -434,44 +471,47 @@ linux_getdents(struct thread *td, struct linux_getdents_args *args) while (len > 0) { bdp = (struct dirent *) inp; reclen = bdp->d_reclen; - linuxreclen = LINUX_RECLEN(bdp->d_namlen); - /* - * No more space in the user supplied dirent buffer. - * Return EINVAL. - */ - if (resid < linuxreclen) { - error = EINVAL; - goto out; + /* Copy a valid entry out. */ + if (bdp->d_fileno != 0) { + linuxreclen = LINUX_RECLEN(bdp->d_namlen); + /* + * No more space in the user supplied dirent buffer. + * Return EINVAL. + */ + if (resid < linuxreclen) { + error = EINVAL; + goto out; + } + + linux_dirent = (struct l_dirent*)lbuf; + linux_dirent->d_ino = bdp->d_fileno; + linux_dirent->d_off = bdp->d_off; + linux_dirent->d_reclen = linuxreclen; + /* + * Copy d_type to last byte of l_dirent buffer + */ + lbuf[linuxreclen - 1] = bdp->d_type; + strlcpy(linux_dirent->d_name, bdp->d_name, + linuxreclen - offsetof(struct l_dirent, d_name)-1); + error = copyout(linux_dirent, outp, linuxreclen); + if (error != 0) + goto out; + retval += linuxreclen; + outp += linuxreclen; + resid -= linuxreclen; } - linux_dirent = (struct l_dirent*)lbuf; - linux_dirent->d_ino = bdp->d_fileno; - linux_dirent->d_off = bdp->d_off; - linux_dirent->d_reclen = linuxreclen; - /* - * Copy d_type to last byte of l_dirent buffer - */ - lbuf[linuxreclen - 1] = bdp->d_type; - strlcpy(linux_dirent->d_name, bdp->d_name, - linuxreclen - offsetof(struct l_dirent, d_name)-1); - error = copyout(linux_dirent, outp, linuxreclen); - if (error != 0) - goto out; - inp += reclen; base += reclen; len -= reclen; - retval += linuxreclen; - outp += linuxreclen; - resid -= linuxreclen; } td->td_retval[0] = retval; out: free(lbuf, M_LINUX); out1: - free(buf, M_LINUX); + free(bufsav, M_LINUX); return (error); } #endif @@ -480,7 +520,7 @@ int linux_getdents64(struct thread *td, struct linux_getdents64_args *args) { struct dirent *bdp; - caddr_t inp, buf; /* BSD-format */ + caddr_t inp, buf, bufsav; /* BSD-format */ int len, reclen; /* BSD-format */ caddr_t outp; /* Linux-format */ int resid, linuxreclen; /* Linux-format */ @@ -489,11 +529,11 @@ linux_getdents64(struct thread *td, struct linux_getdents64_args *args) int buflen, error; size_t retval; - buflen = min(args->count, MAXBSIZE); - buf = malloc(buflen, M_LINUX, M_WAITOK); + buflen = min(roundup2(args->count, DEV_BSIZE), MAXBSIZE); + bufsav = buf = malloc(buflen, M_LINUX, M_WAITOK); - error = kern_getdirentries(td, args->fd, buf, buflen, - &base, NULL, UIO_SYSSPACE); + error = linux_getdirentries(td, args->fd, &buf, buflen, + &base, &len); if (error != 0) { error = linux_getdents_error(td, args->fd, error); goto out1; @@ -502,7 +542,6 @@ linux_getdents64(struct thread *td, struct linux_getdents64_args *args) linux_dirent64 = malloc(LINUX_RECLEN64(LINUX_NAME_MAX), M_LINUX, M_WAITOK | M_ZERO); - len = td->td_retval[0]; inp = buf; outp = (caddr_t)args->dirent; resid = args->count; @@ -511,40 +550,43 @@ linux_getdents64(struct thread *td, struct linux_getdents64_args *args) while (len > 0) { bdp = (struct dirent *) inp; reclen = bdp->d_reclen; - linuxreclen = LINUX_RECLEN64(bdp->d_namlen); - /* - * No more space in the user supplied dirent buffer. - * Return EINVAL. - */ - if (resid < linuxreclen) { - error = EINVAL; - goto out; + /* Copy a valid entry out. */ + if (bdp->d_fileno != 0) { + linuxreclen = LINUX_RECLEN64(bdp->d_namlen); + /* + * No more space in the user supplied dirent buffer. + * Return EINVAL. + */ + if (resid < linuxreclen) { + error = EINVAL; + goto out; + } + + linux_dirent64->d_ino = bdp->d_fileno; + linux_dirent64->d_off = bdp->d_off; + linux_dirent64->d_reclen = linuxreclen; + linux_dirent64->d_type = bdp->d_type; + strlcpy(linux_dirent64->d_name, bdp->d_name, + linuxreclen - offsetof(struct l_dirent64, d_name)); + error = copyout(linux_dirent64, outp, linuxreclen); + if (error != 0) + goto out; + retval += linuxreclen; + outp += linuxreclen; + resid -= linuxreclen; } - linux_dirent64->d_ino = bdp->d_fileno; - linux_dirent64->d_off = bdp->d_off; - linux_dirent64->d_reclen = linuxreclen; - linux_dirent64->d_type = bdp->d_type; - strlcpy(linux_dirent64->d_name, bdp->d_name, - linuxreclen - offsetof(struct l_dirent64, d_name)); - error = copyout(linux_dirent64, outp, linuxreclen); - if (error != 0) - goto out; - inp += reclen; base += reclen; len -= reclen; - retval += linuxreclen; - outp += linuxreclen; - resid -= linuxreclen; } td->td_retval[0] = retval; out: free(linux_dirent64, M_LINUX); out1: - free(buf, M_LINUX); + free(bufsav, M_LINUX); return (error); } @@ -553,22 +595,22 @@ int linux_readdir(struct thread *td, struct linux_readdir_args *args) { struct dirent *bdp; - caddr_t buf; /* BSD-format */ + caddr_t buf, bufsav; /* BSD-format */ int linuxreclen; /* Linux-format */ off_t base; struct l_dirent *linux_dirent; /* Linux-format */ - int buflen, error; + int buflen, error, len; - buflen = sizeof(*bdp); - buf = malloc(buflen, M_LINUX, M_WAITOK); + buflen = DEV_BSIZE; + bufsav = buf = malloc(buflen, M_LINUX, M_WAITOK); - error = kern_getdirentries(td, args->fd, buf, buflen, - &base, NULL, UIO_SYSSPACE); + error = linux_getdirentries(td, args->fd, &buf, buflen, + &base, &len); if (error != 0) { error = linux_getdents_error(td, args->fd, error); goto out; } - if (td->td_retval[0] == 0) + if (len == 0) goto out; linux_dirent = malloc(LINUX_RECLEN(LINUX_NAME_MAX), M_LINUX, @@ -588,7 +630,7 @@ linux_readdir(struct thread *td, struct linux_readdir_args *args) free(linux_dirent, M_LINUX); out: - free(buf, M_LINUX); + free(bufsav, M_LINUX); return (error); } #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */ @@ -769,7 +811,7 @@ linux_rename(struct thread *td, struct linux_rename_args *args) { return (kern_renameat(td, AT_FDCWD, args->from, AT_FDCWD, - args->to, UIO_USERSPACE)); + args->to, UIO_USERSPACE, 0)); } #endif @@ -791,23 +833,34 @@ int linux_renameat2(struct thread *td, struct linux_renameat2_args *args) { int olddfd, newdfd; + u_int atflags; - if (args->flags != 0) { - if (args->flags & ~(LINUX_RENAME_EXCHANGE | - LINUX_RENAME_NOREPLACE | LINUX_RENAME_WHITEOUT)) - return (EINVAL); - if (args->flags & LINUX_RENAME_EXCHANGE && - args->flags & (LINUX_RENAME_NOREPLACE | - LINUX_RENAME_WHITEOUT)) + atflags = 0; + if ((args->flags & ~(LINUX_RENAME_EXCHANGE | + LINUX_RENAME_NOREPLACE | LINUX_RENAME_WHITEOUT)) != 0) + return (EINVAL); + if ((args->flags & LINUX_RENAME_EXCHANGE) != 0 && + (args->flags & (LINUX_RENAME_NOREPLACE | + LINUX_RENAME_WHITEOUT)) != 0) + return (EINVAL); + if ((args->flags & LINUX_RENAME_NOREPLACE) != 0) { + if ((args->flags & (LINUX_RENAME_EXCHANGE | + LINUX_RENAME_WHITEOUT)) != 0) return (EINVAL); -#if 0 + args->flags &= ~LINUX_RENAME_NOREPLACE; + atflags |= AT_RENAME_NOREPLACE; + } + + if (args->flags != 0) { /* * This spams the console on Ubuntu Focal. * - * What's needed here is a general mechanism to let users know - * about missing features without hogging the system. + * What's needed here is a general mechanism to let + * users know about missing features without hogging + * the system. */ - linux_msg(td, "renameat2 unsupported flags 0x%x", +#if 0 + linux_msg(td, "renameat2 unsupported flags %#x", args->flags); #endif return (EINVAL); @@ -816,7 +869,7 @@ linux_renameat2(struct thread *td, struct linux_renameat2_args *args) olddfd = (args->olddfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->olddfd; newdfd = (args->newdfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->newdfd; return (kern_renameat(td, olddfd, args->oldname, newdfd, - args->newname, UIO_USERSPACE)); + args->newname, UIO_USERSPACE, atflags)); } #ifdef LINUX_LEGACY_SYSCALLS @@ -1171,7 +1224,7 @@ linux_oldumount(struct thread *td, struct linux_oldumount_args *args) int linux_umount(struct thread *td, struct linux_umount_args *args) { - int flags; + uint64_t flags; flags = 0; if ((args->flags & LINUX_MNT_FORCE) != 0) { diff --git a/sys/compat/linux/linux_if.c b/sys/compat/linux/linux_if.c index f201f7c4b128..ca4e3a4079ed 100644 --- a/sys/compat/linux/linux_if.c +++ b/sys/compat/linux/linux_if.c @@ -105,12 +105,13 @@ static void linux_ifnet_vnet_uninit(void *arg __unused) { /* - * At a normal vnet shutdown all interfaces are gone at this point. - * But when we kldunload linux.ko, the vnet_deregister_sysuninit() - * would call this function for the default vnet. + * All cloned interfaces are already gone at this point, as well + * as interfaces that were if_vmove'd into this vnet. However, + * if a jail has created IFT_ETHER interfaces in self, or has had + * physical Ethernet drivers attached in self, than we may have + * allocated entries in the unr(9), so clear it to avoid KASSERT. */ - if (IS_DEFAULT_VNET(curvnet)) - clear_unrhdr(V_linux_eth_unr); + clear_unrhdr(V_linux_eth_unr); delete_unrhdr(V_linux_eth_unr); } VNET_SYSUNINIT(linux_ifnet_vnet_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY, diff --git a/sys/compat/linux/linux_ioctl.c b/sys/compat/linux/linux_ioctl.c index ceb17bd040b5..d2fa0331026b 100644 --- a/sys/compat/linux/linux_ioctl.c +++ b/sys/compat/linux/linux_ioctl.c @@ -58,6 +58,7 @@ #include <net/if_types.h> #include <dev/evdev/input.h> +#include <dev/hid/hidraw.h> #include <dev/usb/usb_ioctl.h> #ifdef COMPAT_LINUX32 @@ -113,6 +114,7 @@ DEFINE_LINUX_IOCTL_SET(kcov, KCOV); #ifndef COMPAT_LINUX32 DEFINE_LINUX_IOCTL_SET(nvme, NVME); #endif +DEFINE_LINUX_IOCTL_SET(hidraw, HIDRAW); #undef DEFINE_LINUX_IOCTL_SET @@ -331,6 +333,17 @@ struct linux_termios { unsigned char c_cc[LINUX_NCCS]; }; +struct linux_termios2 { + unsigned int c_iflag; + unsigned int c_oflag; + unsigned int c_cflag; + unsigned int c_lflag; + unsigned char c_line; + unsigned char c_cc[LINUX_NCCS]; + unsigned int c_ispeed; + unsigned int c_ospeed; +}; + struct linux_winsize { unsigned short ws_row, ws_col; unsigned short ws_xpixel, ws_ypixel; @@ -386,7 +399,7 @@ bsd_to_linux_speed(int speed, struct speedtab *table) for ( ; table->sp_speed != -1; table++) if (table->sp_speed == speed) return (table->sp_code); - return (-1); + return (LINUX_BOTHER); } static void @@ -509,6 +522,14 @@ bsd_to_linux_termios(struct termios *bios, struct linux_termios *lios) } static void +bsd_to_linux_termios2(struct termios *bios, struct linux_termios2 *lios2) +{ + bsd_to_linux_termios(bios, (struct linux_termios *)lios2); + lios2->c_ospeed = bios->c_ospeed; + lios2->c_ispeed = bios->c_ispeed; +} + +static void linux_to_bsd_termios(struct linux_termios *lios, struct termios *bios) { int i; @@ -629,6 +650,16 @@ linux_to_bsd_termios(struct linux_termios *lios, struct termios *bios) } static void +linux_to_bsd_termios2(struct linux_termios2 *lios2, struct termios *bios) +{ + linux_to_bsd_termios((struct linux_termios *)lios2, bios); + if ((lios2->c_cflag & LINUX_CBAUD) == LINUX_BOTHER) + bios->c_ospeed = lios2->c_ospeed; + if ((lios2->c_cflag & LINUX_CIBAUD) == LINUX_BOTHER << LINUX_IBSHIFT) + bios->c_ispeed = lios2->c_ispeed; +} + +static void bsd_to_linux_termio(struct termios *bios, struct linux_termio *lio) { struct linux_termios lios; @@ -664,6 +695,7 @@ linux_ioctl_termio(struct thread *td, struct linux_ioctl_args *args) { struct termios bios; struct linux_termios lios; + struct linux_termios2 lios2; struct linux_termio lio; struct file *fp; int error; @@ -1001,6 +1033,43 @@ linux_ioctl_termio(struct thread *td, struct linux_ioctl_args *args) args->cmd = TIOCCBRK; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; + + case LINUX_TCGETS2: + error = fo_ioctl(fp, TIOCGETA, (caddr_t)&bios, td->td_ucred, + td); + if (error) + break; + bsd_to_linux_termios2(&bios, &lios2); + error = copyout(&lios2, (void *)args->arg, sizeof(lios2)); + break; + + case LINUX_TCSETS2: + error = copyin((void *)args->arg, &lios2, sizeof(lios2)); + if (error) + break; + linux_to_bsd_termios2(&lios2, &bios); + error = (fo_ioctl(fp, TIOCSETA, (caddr_t)&bios, td->td_ucred, + td)); + break; + + case LINUX_TCSETSW2: + error = copyin((void *)args->arg, &lios2, sizeof(lios2)); + if (error) + break; + linux_to_bsd_termios2(&lios2, &bios); + error = (fo_ioctl(fp, TIOCSETAW, (caddr_t)&bios, td->td_ucred, + td)); + break; + + case LINUX_TCSETSF2: + error = copyin((void *)args->arg, &lios2, sizeof(lios2)); + if (error) + break; + linux_to_bsd_termios2(&lios2, &bios); + error = (fo_ioctl(fp, TIOCSETAF, (caddr_t)&bios, td->td_ucred, + td)); + break; + case LINUX_TIOCGPTN: { int nb; @@ -3570,6 +3639,55 @@ linux_ioctl_nvme(struct thread *td, struct linux_ioctl_args *args) } #endif +static int +linux_ioctl_hidraw(struct thread *td, struct linux_ioctl_args *args) +{ + int len = (args->cmd & 0x3fff0000) >> 16; + if (len > 8192) + return (EINVAL); + + switch (args->cmd & 0xffff) { + case LINUX_HIDIOCGRDESCSIZE: + args->cmd = HIDIOCGRDESCSIZE; + break; + case LINUX_HIDIOCGRDESC: + args->cmd = HIDIOCGRDESC; + break; + case LINUX_HIDIOCGRAWINFO: + args->cmd = HIDIOCGRAWINFO; + break; + case LINUX_HIDIOCGRAWNAME: + args->cmd = HIDIOCGRAWNAME(len); + break; + case LINUX_HIDIOCGRAWPHYS: + args->cmd = HIDIOCGRAWPHYS(len); + break; + case LINUX_HIDIOCSFEATURE: + args->cmd = HIDIOCSFEATURE(len); + break; + case LINUX_HIDIOCGFEATURE: + args->cmd = HIDIOCGFEATURE(len); + break; + case LINUX_HIDIOCGRAWUNIQ: + args->cmd = HIDIOCGRAWUNIQ(len); + break; + case LINUX_HIDIOCSINPUT: + args->cmd = HIDIOCSINPUT(len); + break; + case LINUX_HIDIOCGINPUT: + args->cmd = HIDIOCGINPUT(len); + break; + case LINUX_HIDIOCSOUTPUT: + args->cmd = HIDIOCSOUTPUT(len); + break; + case LINUX_HIDIOCGOUTPUT: + args->cmd = HIDIOCGOUTPUT(len); + break; + } + + return (sys_ioctl(td, (struct ioctl_args *)args)); +} + /* * main ioctl syscall function */ diff --git a/sys/compat/linux/linux_ioctl.h b/sys/compat/linux/linux_ioctl.h index 8345b7e4b719..116a4e676228 100644 --- a/sys/compat/linux/linux_ioctl.h +++ b/sys/compat/linux/linux_ioctl.h @@ -383,6 +383,11 @@ #define LINUX_TIOCSBRK 0x5427 #define LINUX_TIOCCBRK 0x5428 +#define LINUX_TCGETS2 0x542A +#define LINUX_TCSETS2 0x542B +#define LINUX_TCSETSW2 0x542C +#define LINUX_TCSETSF2 0x542D + #define LINUX_TIOCGPTN 0x5430 #define LINUX_TIOCSPTLCK 0x5431 @@ -501,6 +506,7 @@ #define LINUX_FF1 0x0008000 #define LINUX_CBAUD 0x0000100f +#define LINUX_CIBAUD (LINUX_CBAUD << LINUX_IBSHIFT) #define LINUX_B0 0x00000000 #define LINUX_B50 0x00000001 @@ -537,8 +543,12 @@ #define LINUX_HUPCL 0x00000400 #define LINUX_CLOCAL 0x00000800 +#define LINUX_BOTHER 0x00001000 + #define LINUX_CRTSCTS 0x80000000 +#define LINUX_IBSHIFT 16 + /* Linux c_lflag masks */ #define LINUX_ISIG 0x00000001 #define LINUX_ICANON 0x00000002 @@ -797,6 +807,25 @@ #define LINUX_IOCTL_NVME_MAX LINUX_NVME_IOCTL_RESCAN /* + * hidraw + */ +#define LINUX_HIDIOCGRDESCSIZE 0x4801 +#define LINUX_HIDIOCGRDESC 0x4802 +#define LINUX_HIDIOCGRAWINFO 0x4803 +#define LINUX_HIDIOCGRAWNAME 0x4804 +#define LINUX_HIDIOCGRAWPHYS 0x4805 +#define LINUX_HIDIOCSFEATURE 0x4806 +#define LINUX_HIDIOCGFEATURE 0x4807 +#define LINUX_HIDIOCGRAWUNIQ 0x4808 +#define LINUX_HIDIOCSINPUT 0x4809 +#define LINUX_HIDIOCGINPUT 0x480A +#define LINUX_HIDIOCSOUTPUT 0x480B +#define LINUX_HIDIOCGOUTPUT 0x480C + +#define LINUX_IOCTL_HIDRAW_MIN LINUX_HIDIOCGRDESCSIZE +#define LINUX_IOCTL_HIDRAW_MAX LINUX_HIDIOCGOUTPUT + +/* * Pluggable ioctl handlers */ struct linux_ioctl_args; diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c index 69fb9935a9ae..937b010c8435 100644 --- a/sys/compat/linux/linux_misc.c +++ b/sys/compat/linux/linux_misc.c @@ -750,6 +750,7 @@ linux_common_wait(struct thread *td, idtype_t idtype, int id, int *statusp, error = linux_copyout_rusage(&wru.wru_self, rup); if (error == 0 && infop != NULL && td->td_retval[0] != 0) { sig = bsd_to_linux_signal(siginfo.si_signo); + memset(&lsi, 0, sizeof(lsi)); siginfo_to_lsiginfo(&siginfo, &lsi, sig); error = copyout(&lsi, infop, sizeof(lsi)); } diff --git a/sys/compat/linux/linux_netlink.c b/sys/compat/linux/linux_netlink.c index 6dd2ad7ad8b0..256f188385ce 100644 --- a/sys/compat/linux/linux_netlink.c +++ b/sys/compat/linux/linux_netlink.c @@ -340,7 +340,6 @@ rtnl_if_flags_to_linux(unsigned int if_flags) case IFF_STATICARP: case IFF_STICKYARP: case IFF_DYING: - case IFF_RENAMING: /* No Linux analogue */ break; case IFF_MULTICAST: diff --git a/sys/compat/linux/linux_timer.c b/sys/compat/linux/linux_timer.c index ed9133359302..230be9572b85 100644 --- a/sys/compat/linux/linux_timer.c +++ b/sys/compat/linux/linux_timer.c @@ -131,7 +131,7 @@ linux_timer_settime(struct thread *td, struct linux_timer_settime_args *uap) return (error); error = kern_ktimer_settime(td, uap->timerid, flags, &val, ovalp); if (error == 0 && uap->old != NULL) { - error = native_to_linux_itimerspec(&l_val, &val); + error = native_to_linux_itimerspec(&l_oval, &oval); if (error == 0) error = copyout(&l_oval, uap->old, sizeof(l_oval)); } @@ -158,7 +158,7 @@ linux_timer_settime64(struct thread *td, struct linux_timer_settime64_args *uap) return (error); error = kern_ktimer_settime(td, uap->timerid, flags, &val, ovalp); if (error == 0 && uap->old != NULL) { - error = native_to_linux_itimerspec64(&l_val, &val); + error = native_to_linux_itimerspec64(&l_oval, &oval); if (error == 0) error = copyout(&l_oval, uap->old, sizeof(l_oval)); } diff --git a/sys/compat/linuxkpi/common/include/asm/unaligned.h b/sys/compat/linuxkpi/common/include/asm/unaligned.h index e45846a3b543..6778e9fcede1 100644 --- a/sys/compat/linuxkpi/common/include/asm/unaligned.h +++ b/sys/compat/linuxkpi/common/include/asm/unaligned.h @@ -48,6 +48,13 @@ get_unaligned_le32(const void *p) return (le32_to_cpup((const __le32 *)p)); } +static __inline uint64_t +get_unaligned_le64(const void *p) +{ + + return (le64_to_cpup((const __le64 *)p)); +} + static __inline void put_unaligned_le16(__le16 v, void *p) { diff --git a/sys/compat/linuxkpi/common/include/linux/acpi.h b/sys/compat/linuxkpi/common/include/linux/acpi.h index 3e1ec1b20626..a764a975c983 100644 --- a/sys/compat/linuxkpi/common/include/linux/acpi.h +++ b/sys/compat/linuxkpi/common/include/linux/acpi.h @@ -32,7 +32,7 @@ #include <linux/device.h> #include <linux/uuid.h> -#if defined(__aarch64__) || defined(__amd64__) || defined(__i386__) +#if defined(__aarch64__) || defined(__amd64__) || defined(__i386__) || defined(__riscv) #include <acpi/acpi.h> #include <acpi/acpi_bus.h> diff --git a/sys/compat/linuxkpi/common/include/linux/bcm47xx_nvram.h b/sys/compat/linuxkpi/common/include/linux/bcm47xx_nvram.h new file mode 100644 index 000000000000..744101a2f8b1 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/bcm47xx_nvram.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 Bjoern A. Zeeb + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef _LINUXKPI_LINUX_BCM47XX_NVRAM_H +#define _LINUXKPI_LINUX_BCM47XX_NVRAM_H + +static inline char * +bcm47xx_nvram_get_contents(size_t *x __unused) +{ + return (NULL); +}; + +static inline void +bcm47xx_nvram_release_contents(const char *x __unused) +{ +}; + +#endif /* _LINUXKPI_LINUX_BCM47XX_NVRAM_H */ diff --git a/sys/compat/linuxkpi/common/include/linux/bcma/bcma.h b/sys/compat/linuxkpi/common/include/linux/bcma/bcma.h new file mode 100644 index 000000000000..3840c3a420e5 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/bcma/bcma.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 Bjoern A. Zeeb + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef _LINUXKPI_LINUX_BCMA_BCMA_H +#define _LINUXKPI_LINUX_BCMA_BCMA_H + +#define BCMA_CORE_80211 0x812 +#define BCMA_CORE_ARM_CA7 0x847 +#define BCMA_CORE_ARM_CM3 0x82A +#define BCMA_CORE_ARM_CR4 0x83E +#define BCMA_CORE_CHIPCOMMON 0x800 +#define BCMA_CORE_GCI 0x840 +#define BCMA_CORE_INTERNAL_MEM 0x80E +#define BCMA_CORE_PCIE2 0x83C +#define BCMA_CORE_PMU 0x827 +#define BCMA_CORE_SDIO_DEV 0x829 +#define BCMA_CORE_SYS_MEM 0x849 + +/* XXX not sure where these belong. */ +#define BCMA_CC_CAP_EXT_AOB_PRESENT 0x00000040 +#define BCMA_CC_PMU_CTL_RES_SHIFT 13 +#define BCMA_CC_PMU_CTL_RES_RELOAD 0x2 +#define BCMA_CC_SROM_CONTROL_OTPSEL 0x00000010 +#define BCMA_CC_SROM_CONTROL_OTP_PRESENT 0x00000020 + +#endif /* _LINUXKPI_LINUX_BCMA_BCMA_H */ diff --git a/sys/compat/linuxkpi/common/include/linux/bcma/bcma_regs.h b/sys/compat/linuxkpi/common/include/linux/bcma/bcma_regs.h new file mode 100644 index 000000000000..0a4cdddf7a73 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/bcma/bcma_regs.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2025 Bjoern A. Zeeb + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef _LINUXKPI_LINUX_BCMA_BCMA_REGS_H +#define _LINUXKPI_LINUX_BCMA_BCMA_REGS_H + +#define BCMA_IOCTL 0x0408 +#define BCMA_IOCTL_CLK 0x0001 +#define BCMA_IOCTL_FGC 0x0002 + +#define BCMA_RESET_CTL 0x0800 +#define BCMA_RESET_CTL_RESET 0x0001 + +#endif /* _LINUXKPI_LINUX_BCMA_BCMA_REGS_H */ diff --git a/sys/compat/linuxkpi/common/include/linux/bitops.h b/sys/compat/linuxkpi/common/include/linux/bitops.h index 8fac80820f30..125081ab5b74 100644 --- a/sys/compat/linuxkpi/common/include/linux/bitops.h +++ b/sys/compat/linuxkpi/common/include/linux/bitops.h @@ -51,12 +51,6 @@ #define BITS_PER_TYPE(t) (sizeof(t) * BITS_PER_BYTE) #define BITS_TO_BYTES(n) howmany((n), BITS_PER_BYTE) -#define hweight8(x) bitcount((uint8_t)(x)) -#define hweight16(x) bitcount16(x) -#define hweight32(x) bitcount32(x) -#define hweight64(x) bitcount64(x) -#define hweight_long(x) bitcountl(x) - #if __has_builtin(__builtin_popcountg) #define HWEIGHT8(x) (__builtin_popcountg((uint8_t)(x))) #define HWEIGHT16(x) (__builtin_popcountg((uint16_t)(x))) @@ -70,6 +64,12 @@ #define HWEIGHT64(x) (__const_bitcount64((uint64_t)(x))) #endif +#define hweight8(x) (__builtin_constant_p(x) ? HWEIGHT8(x) : bitcount((uint8_t)(x))) +#define hweight16(x) (__builtin_constant_p(x) ? HWEIGHT16(x) : bitcount16(x)) +#define hweight32(x) (__builtin_constant_p(x) ? HWEIGHT32(x) : bitcount32(x)) +#define hweight64(x) (__builtin_constant_p(x) ? HWEIGHT64(x) : bitcount64(x)) +#define hweight_long(x) bitcountl(x) + static inline int __ffs(int mask) { @@ -437,4 +437,16 @@ sign_extend32(uint32_t value, int index) return ((int32_t)(value << shift) >> shift); } +static inline uint64_t +rol64(uint64_t word, unsigned int shift) +{ + return ((word << (shift & 63)) | (word >> ((-shift) & 63))); +} + +static inline uint32_t +rol32(uint32_t word, unsigned int shift) +{ + return ((word << (shift & 31)) | (word >> ((-shift) & 31))); +} + #endif /* _LINUXKPI_LINUX_BITOPS_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/cec-funcs.h b/sys/compat/linuxkpi/common/include/linux/cec-funcs.h new file mode 100644 index 000000000000..1107b04e4e08 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/cec-funcs.h @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025-2026 The FreeBSD Foundation + * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org> + * + * This software was developed by Jean-Sébastien Pédron under sponsorship + * from the FreeBSD Foundation. + */ + +#ifndef _LINUXKPI_LINUX_CEC_FUNCS_H_ +#define _LINUXKPI_LINUX_CEC_FUNCS_H_ + +#include <linux/cec.h> + +#endif diff --git a/sys/compat/linuxkpi/common/include/linux/cec.h b/sys/compat/linuxkpi/common/include/linux/cec.h index e0854d87d85c..b08d891537a9 100644 --- a/sys/compat/linuxkpi/common/include/linux/cec.h +++ b/sys/compat/linuxkpi/common/include/linux/cec.h @@ -3,6 +3,9 @@ #ifndef _LINUXKPI_LINUX_CEC_H_ #define _LINUXKPI_LINUX_CEC_H_ +#include <linux/types.h> +#include <linux/string.h> + #define CEC_PHYS_ADDR_INVALID 0xffff #endif /* _LINUXKPI_LINUX_CEC_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/cgroup.h b/sys/compat/linuxkpi/common/include/linux/cgroup.h index a9dd22fd0f4c..c75404fd6cf3 100644 --- a/sys/compat/linuxkpi/common/include/linux/cgroup.h +++ b/sys/compat/linuxkpi/common/include/linux/cgroup.h @@ -29,6 +29,15 @@ #ifndef _LINUXKPI_LINUX_CGROUP_H_ #define _LINUXKPI_LINUX_CGROUP_H_ +#include <linux/sched.h> +#include <linux/nodemask.h> +#include <linux/list.h> +#include <linux/rculist.h> +#include <linux/fs.h> +#include <linux/seq_file.h> +#include <linux/jump_label.h> +#include <linux/types.h> +#include <linux/refcount.h> #include <linux/kernel_stat.h> #endif /* _LINUXKPI_LINUX_CGROUP_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/cleanup.h b/sys/compat/linuxkpi/common/include/linux/cleanup.h index 5bb146f082ed..fb21a81f121b 100644 --- a/sys/compat/linuxkpi/common/include/linux/cleanup.h +++ b/sys/compat/linuxkpi/common/include/linux/cleanup.h @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2024-2025 The FreeBSD Foundation + * Copyright (c) 2024-2026 The FreeBSD Foundation * * This software was developed by Björn Zeeb under sponsorship from * the FreeBSD Foundation. @@ -10,18 +10,26 @@ #ifndef _LINUXKPI_LINUX_CLEANUP_H #define _LINUXKPI_LINUX_CLEANUP_H +#include <linux/err.h> + +#define CLEANUP_NAME(_n, _s) __CONCAT(__CONCAT(cleanup_, _n), _s) + #define __cleanup(_f) __attribute__((__cleanup__(_f))) +#define DECLARE(_n, _x) \ + CLEANUP_NAME(_n, _t) _x __cleanup(CLEANUP_NAME(_n, _destroy)) = \ + CLEANUP_NAME(_n, _create) + /* * Note: "_T" are special as they are exposed into common code for * statements. Extra care should be taken when changing the code. */ #define DEFINE_GUARD(_n, _dt, _lock, _unlock) \ \ - typedef _dt guard_ ## _n ## _t; \ + typedef _dt CLEANUP_NAME(_n, _t); \ \ static inline _dt \ - guard_ ## _n ## _create( _dt _T) \ + CLEANUP_NAME(_n, _create)( _dt _T) \ { \ _dt c; \ \ @@ -30,7 +38,7 @@ } \ \ static inline void \ - guard_ ## _n ## _destroy(_dt *t) \ + CLEANUP_NAME(_n, _destroy)(_dt *t) \ { \ _dt _T; \ \ @@ -39,9 +47,10 @@ } /* We need to keep these calls unique. */ +#define _guard(_n, _x) \ + DECLARE(_n, _x) #define guard(_n) \ - guard_ ## _n ## _t guard_ ## _n ## _ ## __COUNTER__ \ - __cleanup(guard_ ## _n ## _destroy) = guard_ ## _n ## _create + _guard(_n, guard_ ## _n ## _ ## __COUNTER__) #define DEFINE_FREE(_n, _t, _f) \ static inline void \ @@ -56,38 +65,100 @@ #define __free(_n) __cleanup(__free_##_n) /* - * Given this is a _0 version it should likely be broken up into parts. - * But we have no idead what a _1, _2, ... version would do different - * until we see a call. - * This is used for a not-real-type (rcu). We use a bool to "simulate" - * the lock held. Also _T still special, may not always be used, so tag - * with __unused (or better the LinuxKPI __maybe_unused). + * Our initial version go broken up. Some simplifications like using + * "bool" for the lock had to be changed to a more general type. + * _T is still special and, like other bits, may not always be used, + * so tag with __unused (or better the LinuxKPI __maybe_unused). */ -#define DEFINE_LOCK_GUARD_0(_n, _lock, _unlock, ...) \ +#define _DEFINE_LOCK_GUARD_0(_n, _lock) \ + static inline CLEANUP_NAME(_n, _t) \ + CLEANUP_NAME(_n, _create)(void) \ + { \ + CLEANUP_NAME(_n, _t) _tmp; \ + CLEANUP_NAME(_n, _t) *_T __maybe_unused; \ + \ + _tmp.lock = (void *)1; \ + _T = &_tmp; \ + _lock; \ + return (_tmp); \ + } + +#define _DEFINE_LOCK_GUARD_1(_n, _type, _lock) \ + static inline CLEANUP_NAME(_n, _t) \ + CLEANUP_NAME(_n, _create)(_type *l) \ + { \ + CLEANUP_NAME(_n, _t) _tmp; \ + CLEANUP_NAME(_n, _t) *_T __maybe_unused; \ \ + _tmp.lock = l; \ + _T = &_tmp; \ + _lock; \ + return (_tmp); \ + } + +#define _GUARD_IS_ERR(_v) \ + ({ \ + uintptr_t x = (uintptr_t)(void *)(_v); \ + IS_ERR_VALUE(x); \ + }) + +#define __is_cond_ptr(_n) \ + CLEANUP_NAME(_n, _is_cond) +#define __guard_ptr(_n) \ + CLEANUP_NAME(_n, _ptr) + +#define _DEFINE_CLEANUP_IS_CONDITIONAL(_n, _b) \ + static const bool CLEANUP_NAME(_n, _is_cond) __maybe_unused = _b + +#define _DEFINE_GUARD_LOCK_PTR(_n, _lp) \ + static inline void * \ + CLEANUP_NAME(_n, _lock_ptr)(CLEANUP_NAME(_n, _t) *_T) \ + { \ + void *_p; \ + \ + _p = (void *)(uintptr_t)*(_lp); \ + if (IS_ERR(_p)) \ + _p = NULL; \ + return (_p); \ + } + +#define _DEFINE_UNLOCK_GUARD(_n, _type, _unlock, ...) \ typedef struct { \ - bool lock; \ + _type *lock; \ __VA_ARGS__; \ - } guard_ ## _n ## _t; \ + } CLEANUP_NAME(_n, _t); \ \ static inline void \ - guard_ ## _n ## _destroy(guard_ ## _n ## _t *_T) \ + CLEANUP_NAME(_n, _destroy)(CLEANUP_NAME(_n, _t) *_T) \ { \ - if (_T->lock) { \ + if (!_GUARD_IS_ERR(_T->lock)) { \ _unlock; \ } \ } \ \ - static inline guard_ ## _n ## _t \ - guard_ ## _n ## _create(void) \ - { \ - guard_ ## _n ## _t _tmp; \ - guard_ ## _n ## _t *_T __maybe_unused; \ - \ - _tmp.lock = true; \ - _T = &_tmp; \ - _lock; \ - return (_tmp); \ - } + _DEFINE_GUARD_LOCK_PTR(_n, &_T->lock) + +#define DEFINE_LOCK_GUARD_0(_n, _lock, _unlock, ...) \ + _DEFINE_CLEANUP_IS_CONDITIONAL(_n, false); \ + _DEFINE_UNLOCK_GUARD(_n, void, _unlock, __VA_ARGS__) \ + _DEFINE_LOCK_GUARD_0(_n, _lock) + +/* This allows the type to be set. */ +#define DEFINE_LOCK_GUARD_1(_n, _t, _lock, _unlock, ...) \ + _DEFINE_CLEANUP_IS_CONDITIONAL(_n, false); \ + _DEFINE_UNLOCK_GUARD(_n, _t, _unlock, __VA_ARGS__) \ + _DEFINE_LOCK_GUARD_1(_n, _t, _lock) + +#define _scoped_guard(_n, _l, ...) \ + for (DECLARE(_n, _scoped)(__VA_ARGS__); \ + 1 /*__guard_ptr(_n)(&_scoped) || !__is_cond_ptr(_n) */; \ + ({ goto _l; })) \ + if (0) { \ +_l: \ + break; \ + } else + +#define scoped_guard(_n, ...) \ + _scoped_guard(_n, ___label_ ## __COUNTER__, ##__VA_ARGS__) #endif /* _LINUXKPI_LINUX_CLEANUP_H */ diff --git a/sys/compat/linuxkpi/common/include/linux/compiler.h b/sys/compat/linuxkpi/common/include/linux/compiler.h index 4146c829b936..90d907dd4d45 100644 --- a/sys/compat/linuxkpi/common/include/linux/compiler.h +++ b/sys/compat/linuxkpi/common/include/linux/compiler.h @@ -33,48 +33,18 @@ #include <sys/cdefs.h> #include <sys/endian.h> -#define __user -#define __kernel -#define __safe -#define __force -#define __nocast -#define __iomem -#define __chk_user_ptr(x) ((void)0) -#define __chk_io_ptr(x) ((void)0) -#define __builtin_warning(x, y...) (1) -#define __acquires(x) -#define __releases(x) -#define __acquire(x) do { } while (0) -#define __release(x) do { } while (0) -#define __cond_lock(x,c) (c) +#include <compat/linuxkpi/common/include/linux/compiler_types.h> + #define __bitwise #define __devinitdata -#ifndef __deprecated -#define __deprecated -#endif #define __init #define __initconst #define __devinit #define __devexit #define __exit -#define __rcu -#define __percpu -#define __weak __weak_symbol -#define __malloc -#define __attribute_const__ __attribute__((__const__)) -#undef __always_inline -#define __always_inline inline -#define noinline __noinline -#define noinline_for_stack __noinline #define ____cacheline_aligned __aligned(CACHE_LINE_SIZE) #define ____cacheline_aligned_in_smp __aligned(CACHE_LINE_SIZE) -#define fallthrough /* FALLTHROUGH */ do { } while(0) -#if __has_attribute(__nonstring__) -#define __nonstring __attribute__((__nonstring__)) -#else -#define __nonstring -#endif #if __has_attribute(__counted_by__) #define __counted_by(_x) __attribute__((__counted_by__(_x))) #else @@ -93,24 +63,12 @@ #define typeof(x) __typeof(x) #define uninitialized_var(x) x = x -#define __maybe_unused __unused -#define __always_unused __unused -#define __must_check __result_use_check - -#define __printf(a,b) __printflike(a,b) - -#define __diag_push() -#define __diag_pop() -#define __diag_ignore_all(...) #define barrier() __asm__ __volatile__("": : :"memory") #define lower_32_bits(n) ((u32)(n)) #define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) -#define ___PASTE(a,b) a##b -#define __PASTE(a,b) ___PASTE(a,b) - #define WRITE_ONCE(x,v) do { \ barrier(); \ (*(volatile __typeof(x) *)(uintptr_t)&(x)) = (v); \ @@ -129,8 +87,6 @@ #define lockless_dereference(p) READ_ONCE(p) #define _AT(T,X) ((T)(X)) - -#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) #define __must_be_array(a) __same_type(a, &(a)[0]) #define sizeof_field(_s, _m) sizeof(((_s *)0)->_m) diff --git a/sys/compat/linuxkpi/common/include/linux/compiler_attributes.h b/sys/compat/linuxkpi/common/include/linux/compiler_attributes.h new file mode 100644 index 000000000000..42908bb6c2b5 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/compiler_attributes.h @@ -0,0 +1,49 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2010 Isilon Systems, Inc. + * Copyright (c) 2010 iX Systems, Inc. + * Copyright (c) 2010 Panasas, Inc. + * Copyright (c) 2013-2016 Mellanox Technologies, Ltd. + * Copyright (c) 2015 François Tigeot + * All rights reserved. + */ + +#ifndef _LINUXKPI_LINUX_COMPILER_ATTRIBUTES_H_ +#define _LINUXKPI_LINUX_COMPILER_ATTRIBUTES_H_ + +#include <sys/cdefs.h> + +#define __attribute_const__ __attribute__((__const__)) + +#ifndef __deprecated +#define __deprecated +#endif + +#define fallthrough /* FALLTHROUGH */ do { } while(0) + +#undef __always_inline +#define __always_inline inline + +#define __printf(a,b) __printflike(a,b) + +#define __malloc + +#define noinline __noinline + +#if __has_attribute(__nonstring__) +#define __nonstring __attribute__((__nonstring__)) +#else +#define __nonstring +#endif + +#define noinline_for_stack __noinline + +#define __maybe_unused __unused +#define __always_unused __unused + +#define __must_check __result_use_check + +#define __weak __weak_symbol + +#endif diff --git a/sys/compat/linuxkpi/common/include/linux/compiler_types.h b/sys/compat/linuxkpi/common/include/linux/compiler_types.h new file mode 100644 index 000000000000..7151c03de690 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/compiler_types.h @@ -0,0 +1,45 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2010 Isilon Systems, Inc. + * Copyright (c) 2010 iX Systems, Inc. + * Copyright (c) 2010 Panasas, Inc. + * Copyright (c) 2013-2016 Mellanox Technologies, Ltd. + * Copyright (c) 2015 François Tigeot + * All rights reserved. + */ + +#ifndef _LINUXKPI_LINUX_COMPILER_TYPES_H_ +#define _LINUXKPI_LINUX_COMPILER_TYPES_H_ + +#include <sys/cdefs.h> + +#include <compat/linuxkpi/common/include/linux/compiler_attributes.h> + +#define __kernel +#define __user +#define __iomem +#define __percpu +#define __rcu +#define __chk_user_ptr(x) ((void)0) +#define __chk_io_ptr(x) ((void)0) +#define __acquires(x) +#define __releases(x) +#define __acquire(x) do { } while (0) +#define __release(x) do { } while (0) +#define __cond_lock(x,c) (c) +#define __force +#define __nocast +#define __safe +#define __builtin_warning(x, y...) (1) + +#define ___PASTE(a,b) a##b +#define __PASTE(a,b) ___PASTE(a,b) + +#define __diag_push() +#define __diag_pop() +#define __diag_ignore_all(...) + +#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) + +#endif diff --git a/sys/compat/linuxkpi/common/include/linux/dma-mapping.h b/sys/compat/linuxkpi/common/include/linux/dma-mapping.h index 2d8e1196d3d3..5e5d40ef8339 100644 --- a/sys/compat/linuxkpi/common/include/linux/dma-mapping.h +++ b/sys/compat/linuxkpi/common/include/linux/dma-mapping.h @@ -96,6 +96,8 @@ void *linux_dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag); void *linuxkpi_dmam_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag); +void linuxkpi_dmam_free_coherent(struct device *dev, size_t size, + void *addr, dma_addr_t dma_handle); dma_addr_t linux_dma_map_phys(struct device *dev, vm_paddr_t phys, size_t len); /* backward compat */ dma_addr_t lkpi_dma_map_phys(struct device *, vm_paddr_t, size_t, enum dma_data_direction, unsigned long); @@ -104,10 +106,10 @@ void lkpi_dma_unmap(struct device *, dma_addr_t, size_t, enum dma_data_direction, unsigned long); int linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents, enum dma_data_direction direction, - unsigned long attrs __unused); + unsigned long attrs); void linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg, int nents __unused, enum dma_data_direction direction, - unsigned long attrs __unused); + unsigned long attrs); void linuxkpi_dma_sync(struct device *, dma_addr_t, size_t, bus_dmasync_op_t); static inline int @@ -181,6 +183,13 @@ dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, kmem_free(cpu_addr, size); } +static inline void +dmam_free_coherent(struct device *dev, size_t size, void *addr, + dma_addr_t dma_handle) +{ + linuxkpi_dmam_free_coherent(dev, size, addr, dma_handle); +} + static inline dma_addr_t dma_map_page_attrs(struct device *dev, struct page *page, size_t offset, size_t size, enum dma_data_direction direction, unsigned long attrs) @@ -192,10 +201,10 @@ dma_map_page_attrs(struct device *dev, struct page *page, size_t offset, /* linux_dma_(un)map_sg_attrs does not support attrs yet */ #define dma_map_sg_attrs(dev, sgl, nents, dir, attrs) \ - linux_dma_map_sg_attrs(dev, sgl, nents, dir, 0) + linux_dma_map_sg_attrs(dev, sgl, nents, dir, attrs) #define dma_unmap_sg_attrs(dev, sg, nents, dir, attrs) \ - linux_dma_unmap_sg_attrs(dev, sg, nents, dir, 0) + linux_dma_unmap_sg_attrs(dev, sg, nents, dir, attrs) static inline dma_addr_t dma_map_page(struct device *dev, struct page *page, @@ -352,10 +361,10 @@ dma_max_mapping_size(struct device *dev) } #define dma_map_single_attrs(dev, ptr, size, dir, attrs) \ - _dma_map_single_attrs(dev, ptr, size, dir, 0) + _dma_map_single_attrs(dev, ptr, size, dir, attrs) #define dma_unmap_single_attrs(dev, dma_addr, size, dir, attrs) \ - _dma_unmap_single_attrs(dev, dma_addr, size, dir, 0) + _dma_unmap_single_attrs(dev, dma_addr, size, dir, attrs) #define dma_map_single(d, a, s, r) dma_map_single_attrs(d, a, s, r, 0) #define dma_unmap_single(d, a, s, r) dma_unmap_single_attrs(d, a, s, r, 0) diff --git a/sys/compat/linuxkpi/common/include/linux/eventfd.h b/sys/compat/linuxkpi/common/include/linux/eventfd.h new file mode 100644 index 000000000000..d167d4b7d189 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/eventfd.h @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 2025 The FreeBSD Foundation + * Copyright (c) 2025 Jean-Sébastien Pédron + * + * This software was developed by Jean-Sébastien Pédron 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. + */ + +#ifndef _LINUXKPI_LINUX_EVENTFD_H_ +#define _LINUXKPI_LINUX_EVENTFD_H_ + +#include <sys/eventfd.h> + +#include <linux/wait.h> +#include <linux/err.h> +#include <linux/percpu-defs.h> +#include <linux/percpu.h> +#include <linux/sched.h> + +/* + * Linux uses `struct eventfd_ctx`, but FreeBSD defines `struct eventfd`. Here, + * we define a synonym to the FreeBSD structure. This allows to keep Linux code + * unmodified. + */ +#define eventfd_ctx eventfd + +#define eventfd_ctx_fdget lkpi_eventfd_ctx_fdget +struct eventfd_ctx *lkpi_eventfd_ctx_fdget(int fd); + +#define eventfd_ctx_put lkpi_eventfd_ctx_put +void lkpi_eventfd_ctx_put(struct eventfd_ctx *ctx); + +#endif /* _LINUXKPI_LINUX_EVENTFD_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/file.h b/sys/compat/linuxkpi/common/include/linux/file.h index f6e988c2d88e..be12d5f1bccf 100644 --- a/sys/compat/linuxkpi/common/include/linux/file.h +++ b/sys/compat/linuxkpi/common/include/linux/file.h @@ -39,6 +39,11 @@ #include <linux/fs.h> #include <linux/slab.h> +#include <linux/compiler.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/cleanup.h> + struct linux_file; #undef file diff --git a/sys/compat/linuxkpi/common/include/linux/font.h b/sys/compat/linuxkpi/common/include/linux/font.h new file mode 100644 index 000000000000..45daa00b61f0 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/font.h @@ -0,0 +1,33 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025-2026 The FreeBSD Foundation + * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org> + * + * This software was developed by Jean-Sébastien Pédron under sponsorship + * from the FreeBSD Foundation. + */ + +#ifndef _LINUXKPI_LINUX_FONT_H_ +#define _LINUXKPI_LINUX_FONT_H_ + +#include <linux/types.h> + +struct font_desc { + const char *name; + const void *data; + int idx; + unsigned int width; + unsigned int height; + unsigned int charcount; + int pref; +}; + +static inline const struct font_desc * +get_default_font(int xres, int yres, unsigned long *font_w, + unsigned long *font_h) +{ + return (NULL); +} + +#endif diff --git a/sys/compat/linuxkpi/common/include/linux/fs.h b/sys/compat/linuxkpi/common/include/linux/fs.h index f1568ad6282d..7e28be070850 100644 --- a/sys/compat/linuxkpi/common/include/linux/fs.h +++ b/sys/compat/linuxkpi/common/include/linux/fs.h @@ -364,27 +364,23 @@ static inline ssize_t simple_read_from_buffer(void __user *dest, size_t read_size, loff_t *ppos, void *orig, size_t buf_size) { - void *p, *read_pos = ((char *) orig) + *ppos; + void *read_pos = ((char *) orig) + *ppos; size_t buf_remain = buf_size - *ppos; + ssize_t num_read; - if (buf_remain < 0 || buf_remain > buf_size) - return -EINVAL; + if (*ppos >= buf_size || read_size == 0) + return (0); if (read_size > buf_remain) read_size = buf_remain; - /* - * XXX At time of commit only debugfs consumers could be - * identified. If others will use this function we may - * have to revise this: normally we would call copy_to_user() - * here but lindebugfs will return the result and the - * copyout is done elsewhere for us. - */ - p = memcpy(dest, read_pos, read_size); - if (p != NULL) - *ppos += read_size; - - return (read_size); + /* copy_to_user returns number of bytes NOT read */ + num_read = read_size - copy_to_user(dest, read_pos, read_size); + if (num_read == 0) + return -EFAULT; + *ppos += num_read; + + return (num_read); } MALLOC_DECLARE(M_LSATTR); @@ -415,11 +411,13 @@ int simple_attr_open(struct inode *inode, struct file *filp, int simple_attr_release(struct inode *inode, struct file *filp); -ssize_t simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t *ppos); +ssize_t simple_attr_read(struct file *filp, char __user *buf, size_t read_size, + loff_t *ppos); -ssize_t simple_attr_write(struct file *filp, const char *buf, size_t write_size, loff_t *ppos); +ssize_t simple_attr_write(struct file *filp, const char __user *buf, + size_t write_size, loff_t *ppos); -ssize_t simple_attr_write_signed(struct file *filp, const char *buf, +ssize_t simple_attr_write_signed(struct file *filp, const char __user *buf, size_t write_size, loff_t *ppos); #endif /* _LINUXKPI_LINUX_FS_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/hardirq.h b/sys/compat/linuxkpi/common/include/linux/hardirq.h index f79451dd0d35..c6cbf1a34f14 100644 --- a/sys/compat/linuxkpi/common/include/linux/hardirq.h +++ b/sys/compat/linuxkpi/common/include/linux/hardirq.h @@ -31,6 +31,7 @@ #include <linux/types.h> #include <linux/lockdep.h> +#include <linux/preempt.h> #include <sys/param.h> #include <sys/bus.h> diff --git a/sys/compat/linuxkpi/common/include/linux/highmem.h b/sys/compat/linuxkpi/common/include/linux/highmem.h index 58a9cdcdf60f..dc1c4fe2f299 100644 --- a/sys/compat/linuxkpi/common/include/linux/highmem.h +++ b/sys/compat/linuxkpi/common/include/linux/highmem.h @@ -45,6 +45,7 @@ #include <linux/mm.h> #include <linux/page.h> +#include <linux/hardirq.h> #define PageHighMem(p) (0) diff --git a/sys/compat/linuxkpi/common/include/linux/i2c.h b/sys/compat/linuxkpi/common/include/linux/i2c.h index f24d282586f6..a6a4ee85d584 100644 --- a/sys/compat/linuxkpi/common/include/linux/i2c.h +++ b/sys/compat/linuxkpi/common/include/linux/i2c.h @@ -31,7 +31,14 @@ #include <sys/errno.h> #include <sys/systm.h> +#include <linux/bits.h> +#include <linux/mod_devicetable.h> #include <linux/device.h> +#include <linux/sched.h> +#include <linux/mutex.h> +#include <linux/regulator/consumer.h> +#include <linux/irqdomain.h> +#include <linux/of.h> #define I2C_MAX_ADAPTER_NAME_LENGTH 32 diff --git a/sys/compat/linuxkpi/common/include/linux/ieee80211.h b/sys/compat/linuxkpi/common/include/linux/ieee80211.h index 12160df43915..d1eba94a3ad8 100644 --- a/sys/compat/linuxkpi/common/include/linux/ieee80211.h +++ b/sys/compat/linuxkpi/common/include/linux/ieee80211.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2020-2025 The FreeBSD Foundation + * Copyright (c) 2020-2026 The FreeBSD Foundation * * This software was developed by Björn Zeeb under sponsorship from * the FreeBSD Foundation. @@ -51,8 +51,16 @@ extern int linuxkpi_debug_80211; #define IMPROVE(fmt, ...) if (linuxkpi_debug_80211 & D80211_IMPROVE) \ printf("%s:%d: XXX LKPI80211 IMPROVE " fmt "\n", __func__, __LINE__, ##__VA_ARGS__) - -/* 9.4.2.55 Management MIC element (CMAC-256, GMAC-128, and GMAC-256). */ +/* 802.11-2024, 9.4.2.53 MME. */ +/* BIP-CMAC-128 */ +struct ieee80211_mmie { + uint8_t element_id; + uint8_t length; + uint16_t key_id; + uint8_t ipn[6]; + uint8_t mic[8]; +}; +/* BIP-CMAC-256, BIP-GMAC-128, BIP-GMAC-256 */ struct ieee80211_mmie_16 { uint8_t element_id; uint8_t length; @@ -108,7 +116,18 @@ struct ieee80211_mmie_16 { #define IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT 0x0100 enum ieee80211_rate_flags { - IEEE80211_RATE_SHORT_PREAMBLE = BIT(0), + IEEE80211_RATE_SHORT_PREAMBLE = BIT(0), /* 2.4Ghz, CCK */ + IEEE80211_RATE_SUPPORTS_5MHZ = BIT(1), + IEEE80211_RATE_SUPPORTS_10MHZ = BIT(2), + IEEE80211_RATE_ERP_G = BIT(3), + + /* + * According to documentation these are flags initialized internally. + * See lkpi_wiphy_band_annotate(). + */ + IEEE80211_RATE_MANDATORY_A = BIT(4), + IEEE80211_RATE_MANDATORY_G = BIT(5), + IEEE80211_RATE_MANDATORY_B = BIT(6), }; enum ieee80211_rate_control_changed_flags { @@ -200,6 +219,7 @@ enum ieee80211_min_mpdu_start_spacing { #define IEEE80211_FCTL_TODS (IEEE80211_FC1_DIR_TODS << 8) #define IEEE80211_FCTL_MOREFRAGS (IEEE80211_FC1_MORE_FRAG << 8) #define IEEE80211_FCTL_PM (IEEE80211_FC1_PWR_MGT << 8) +#define IEEE80211_FCTL_MOREDATA (IEEE80211_FC1_MORE_DATA << 8) #define IEEE80211_FTYPE_MGMT IEEE80211_FC0_TYPE_MGT #define IEEE80211_FTYPE_CTL IEEE80211_FC0_TYPE_CTL @@ -461,18 +481,6 @@ enum ieee80211_tx_control_flags { IEEE80211_TX_CTRL_MLO_LINK = 0xF0000000, /* This is IEEE80211_LINK_UNSPECIFIED on the high bits. */ }; -enum ieee80211_tx_rate_flags { - /* XXX TODO .. right shift numbers */ - IEEE80211_TX_RC_40_MHZ_WIDTH = BIT(0), - IEEE80211_TX_RC_80_MHZ_WIDTH = BIT(1), - IEEE80211_TX_RC_160_MHZ_WIDTH = BIT(2), - IEEE80211_TX_RC_GREEN_FIELD = BIT(3), - IEEE80211_TX_RC_MCS = BIT(4), - IEEE80211_TX_RC_SHORT_GI = BIT(5), - IEEE80211_TX_RC_VHT_MCS = BIT(6), - IEEE80211_TX_RC_USE_SHORT_PREAMBLE = BIT(7), -}; - #define IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED -128 #define IEEE80211_HT_CTL_LEN 4 diff --git a/sys/compat/linuxkpi/common/include/linux/kfifo.h b/sys/compat/linuxkpi/common/include/linux/kfifo.h index d2f570781661..fbe16e22683e 100644 --- a/sys/compat/linuxkpi/common/include/linux/kfifo.h +++ b/sys/compat/linuxkpi/common/include/linux/kfifo.h @@ -33,8 +33,26 @@ #include <linux/slab.h> #include <linux/gfp.h> -#define INIT_KFIFO(x) 0 -#define DECLARE_KFIFO(x, y, z) +/* + * INIT_KFIFO() is used to initialize the structure declared with + * DECLARE_KFIFO(). It doesn't work with DECLARE_KFIFO_PTR(). + */ +#define INIT_KFIFO(_kf) \ + ({ \ + (_kf).total = nitems((_kf).head); \ + (_kf).count = 0; \ + (_kf).first = 0; \ + (_kf).last = 0; \ + }) + +#define DECLARE_KFIFO(_name, _type, _size) \ + struct kfifo_ ## _name { \ + size_t total; \ + size_t count; \ + size_t first; \ + size_t last; \ + _type head[_size]; \ + } _name #define DECLARE_KFIFO_PTR(_name, _type) \ struct kfifo_ ## _name { \ diff --git a/sys/compat/linuxkpi/common/include/linux/kmsg_dump.h b/sys/compat/linuxkpi/common/include/linux/kmsg_dump.h new file mode 100644 index 000000000000..25f96b304f59 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/kmsg_dump.h @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025-2026 The FreeBSD Foundation + * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org> + * + * This software was developed by Jean-Sébastien Pédron under sponsorship + * from the FreeBSD Foundation. + */ + +#ifndef _LINUXKPI_LINUX_KMSG_DUMP_H_ +#define _LINUXKPI_LINUX_KMSG_DUMP_H_ + +#include <linux/errno.h> +#include <linux/list.h> + +#include <linux/kernel.h> /* For pr_debug() */ + +enum kmsg_dump_reason { + KMSG_DUMP_UNDEF, + KMSG_DUMP_PANIC, + KMSG_DUMP_OOPS, + KMSG_DUMP_EMERG, + KMSG_DUMP_SHUTDOWN, + KMSG_DUMP_MAX +}; + +struct kmsg_dumper { + struct list_head list; + void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason); + enum kmsg_dump_reason max_reason; + bool registered; +}; + +static inline int +kmsg_dump_register(struct kmsg_dumper *dumper) +{ + pr_debug("TODO"); + + return (-EINVAL); +} + +static inline int +kmsg_dump_unregister(struct kmsg_dumper *dumper) +{ + pr_debug("TODO"); + + return (-EINVAL); +} + +#endif diff --git a/sys/compat/linuxkpi/common/include/linux/kstrtox.h b/sys/compat/linuxkpi/common/include/linux/kstrtox.h index 5da99de24197..05bf94dd375d 100644 --- a/sys/compat/linuxkpi/common/include/linux/kstrtox.h +++ b/sys/compat/linuxkpi/common/include/linux/kstrtox.h @@ -74,14 +74,17 @@ static inline int kstrtoul(const char *cp, unsigned int base, unsigned long *res) { char *end; + unsigned long temp; - *res = strtoul(cp, &end, base); + temp = strtoul(cp, &end, base); /* skip newline character, if any */ if (*end == '\n') end++; if (*cp == 0 || *end != 0) return (-EINVAL); + + *res = temp; return (0); } @@ -89,14 +92,17 @@ static inline int kstrtol(const char *cp, unsigned int base, long *res) { char *end; + long temp; - *res = strtol(cp, &end, base); + temp = strtol(cp, &end, base); /* skip newline character, if any */ if (*end == '\n') end++; if (*cp == 0 || *end != 0) return (-EINVAL); + + *res = temp; return (0); } @@ -106,7 +112,7 @@ kstrtoint(const char *cp, unsigned int base, int *res) char *end; long temp; - *res = temp = strtol(cp, &end, base); + temp = strtol(cp, &end, base); /* skip newline character, if any */ if (*end == '\n') @@ -115,6 +121,8 @@ kstrtoint(const char *cp, unsigned int base, int *res) return (-EINVAL); if (temp != (int)temp) return (-ERANGE); + + *res = (int)temp; return (0); } @@ -124,7 +132,7 @@ kstrtouint(const char *cp, unsigned int base, unsigned int *res) char *end; unsigned long temp; - *res = temp = strtoul(cp, &end, base); + temp = strtoul(cp, &end, base); /* skip newline character, if any */ if (*end == '\n') @@ -133,6 +141,8 @@ kstrtouint(const char *cp, unsigned int base, unsigned int *res) return (-EINVAL); if (temp != (unsigned int)temp) return (-ERANGE); + + *res = (unsigned int)temp; return (0); } @@ -142,7 +152,7 @@ kstrtou8(const char *cp, unsigned int base, uint8_t *res) char *end; unsigned long temp; - *res = temp = strtoul(cp, &end, base); + temp = strtoul(cp, &end, base); /* skip newline character, if any */ if (*end == '\n') @@ -151,6 +161,8 @@ kstrtou8(const char *cp, unsigned int base, uint8_t *res) return (-EINVAL); if (temp != (uint8_t)temp) return (-ERANGE); + + *res = (uint8_t)temp; return (0); } @@ -160,7 +172,7 @@ kstrtou16(const char *cp, unsigned int base, uint16_t *res) char *end; unsigned long temp; - *res = temp = strtoul(cp, &end, base); + temp = strtoul(cp, &end, base); /* skip newline character, if any */ if (*end == '\n') @@ -169,20 +181,20 @@ kstrtou16(const char *cp, unsigned int base, uint16_t *res) return (-EINVAL); if (temp != (uint16_t)temp) return (-ERANGE); + + *res = (uint16_t)temp; return (0); } static inline int kstrtou32(const char *cp, unsigned int base, uint32_t *res) { - return (kstrtouint(cp, base, res)); } static inline int kstrtos32(const char *cp, unsigned int base, int32_t *res) { - return (kstrtoint(cp, base, res)); } @@ -190,14 +202,17 @@ static inline int kstrtos64(const char *cp, unsigned int base, int64_t *res) { char *end; + quad_t temp; - *res = strtoq(cp, &end, base); + temp = strtoq(cp, &end, base); /* skip newline character, if any */ if (*end == '\n') end++; if (*cp == 0 || *end != 0) return (-EINVAL); + + *res = (int64_t)temp; return (0); } @@ -208,17 +223,20 @@ kstrtoll(const char *cp, unsigned int base, long long *res) } static inline int -kstrtou64(const char *cp, unsigned int base, u64 *res) +kstrtou64(const char *cp, unsigned int base, uint64_t *res) { char *end; + u_quad_t temp; - *res = strtouq(cp, &end, base); + temp = strtouq(cp, &end, base); /* skip newline character, if any */ if (*end == '\n') end++; if (*cp == 0 || *end != 0) return (-EINVAL); + + *res = (uint64_t)temp; return (0); } @@ -231,22 +249,16 @@ kstrtoull(const char *cp, unsigned int base, unsigned long long *res) static inline int kstrtobool(const char *s, bool *res) { - int len; - - if (s == NULL || (len = strlen(s)) == 0 || res == NULL) + if (s == NULL || *s == '\0') return (-EINVAL); - /* skip newline character, if any */ - if (s[len - 1] == '\n') - len--; - - if (len == 1 && strchr("yY1", s[0]) != NULL) + if (strchr("eEtTyY1", s[0]) != NULL) *res = true; - else if (len == 1 && strchr("nN0", s[0]) != NULL) + else if (strchr("dDfFnN0", s[0]) != NULL) *res = false; - else if (strncasecmp("on", s, len) == 0) + else if (strncasecmp("on", s, 2) == 0) *res = true; - else if (strncasecmp("off", s, len) == 0) + else if (strncasecmp("of", s, 2) == 0) *res = false; else return (-EINVAL); @@ -302,7 +314,6 @@ static inline int kstrtou32_from_user(const char __user *s, size_t count, unsigned int base, unsigned int *p) { - return (kstrtouint_from_user(s, count, base, p)); } diff --git a/sys/compat/linuxkpi/common/include/linux/memcontrol.h b/sys/compat/linuxkpi/common/include/linux/memcontrol.h new file mode 100644 index 000000000000..57fadf9af60f --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/memcontrol.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2025 The FreeBSD Foundation + * Copyright (c) 2025 Jean-Sébastien Pédron <dumbbell@FreeBSD.org> + * + * This software was developed by Jean-Sébastien Pédron 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. + */ + + +#ifndef _LINUXKPI_LINUX_MEMCONTROL_H_ +#define _LINUXKPI_LINUX_MEMCONTROL_H_ + +#include <linux/cgroup.h> +#include <linux/hardirq.h> +#include <linux/jump_label.h> +#include <linux/kernel.h> +#include <linux/eventfd.h> +#include <linux/mm.h> +#include <linux/page-flags.h> +#include <linux/shrinker.h> + +#endif /* defined(_LINUXKPI_LINUX_MEMCONTROL_H_) */ diff --git a/sys/compat/linuxkpi/common/include/linux/minmax.h b/sys/compat/linuxkpi/common/include/linux/minmax.h index d48958f0899f..fb8eb6f704b4 100644 --- a/sys/compat/linuxkpi/common/include/linux/minmax.h +++ b/sys/compat/linuxkpi/common/include/linux/minmax.h @@ -71,4 +71,7 @@ b = _swap_tmp; \ } while (0) +/* XXX would have to make sure both are unsigned. */ +#define umin(x, y) MIN(x, y) + #endif /* _LINUXKPI_LINUX_MINMAX_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/mod_devicetable.h b/sys/compat/linuxkpi/common/include/linux/mod_devicetable.h index 87bd6ec24bce..df7c2472861b 100644 --- a/sys/compat/linuxkpi/common/include/linux/mod_devicetable.h +++ b/sys/compat/linuxkpi/common/include/linux/mod_devicetable.h @@ -80,4 +80,10 @@ struct dmi_system_id { #define ACPI_ID_LEN 16 +/* ----------------------------------------------------------------------------- + * USB + */ +/* struct usb_device_id is defined in sys/dev/usb/usbdi.h. */ +/* MODULE_DEVICE_TABLE_BUS_usb we have in usb.h. */ + #endif /* __LINUXKPI_LINUX_MOD_DEVICETABLE_H__ */ diff --git a/sys/compat/linuxkpi/common/include/linux/module.h b/sys/compat/linuxkpi/common/include/linux/module.h index 079dacf8df6c..fbe57cbbed82 100644 --- a/sys/compat/linuxkpi/common/include/linux/module.h +++ b/sys/compat/linuxkpi/common/include/linux/module.h @@ -53,6 +53,24 @@ #define MODULE_SUPPORTED_DEVICE(name) #define MODULE_IMPORT_NS(_name) +/* Linux has an empty element at the end of the ID table -> nitems() - 1. */ +#define MODULE_DEVICE_TABLE(_bus, _table) \ + \ +static device_method_t _ ## _bus ## _ ## _table ## _methods[] = { \ + DEVMETHOD_END \ +}; \ + \ +static driver_t _ ## _bus ## _ ## _table ## _driver = { \ + "lkpi_" #_bus #_table, \ + _ ## _bus ## _ ## _table ## _methods, \ + 0 \ +}; \ + \ +DRIVER_MODULE(lkpi_ ## _table, _bus, _ ## _bus ## _ ## _table ## _driver,\ + 0, 0); \ + \ +MODULE_DEVICE_TABLE_BUS_ ## _bus(_bus, _table) + /* * THIS_MODULE is used to differentiate modules on Linux. We currently * completely stub out any Linux struct module usage, but THIS_MODULE is still diff --git a/sys/compat/linuxkpi/common/include/linux/netdevice.h b/sys/compat/linuxkpi/common/include/linux/netdevice.h index cf27753bcb80..dfed5fbd61b4 100644 --- a/sys/compat/linuxkpi/common/include/linux/netdevice.h +++ b/sys/compat/linuxkpi/common/include/linux/netdevice.h @@ -160,6 +160,30 @@ struct net_device { #define SET_NETDEV_DEV(_ndev, _dev) (_ndev)->dev.parent = _dev; +enum net_device_path_type { + DEV_PATH_MTK_WDMA, +}; + +struct net_device_path { + enum net_device_path_type type; + const struct net_device *dev; + /* We assume there's a struct per type. */ + union { + struct { + uint16_t wcid; + uint8_t wdma_idx; + uint8_t queue; + uint8_t bss; + uint8_t amsdu; + } mtk_wdma; + }; +}; + +struct net_device_path_ctx { + const struct net_device *dev; +}; + + /* -------------------------------------------------------------------------- */ /* According to linux::ipoib_main.c. */ struct netdev_notifier_info { diff --git a/sys/compat/linuxkpi/common/include/linux/notifier.h b/sys/compat/linuxkpi/common/include/linux/notifier.h index 9302a1ce4606..4fe43255c648 100644 --- a/sys/compat/linuxkpi/common/include/linux/notifier.h +++ b/sys/compat/linuxkpi/common/include/linux/notifier.h @@ -32,6 +32,11 @@ #include <sys/types.h> #include <sys/eventhandler.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/rwsem.h> +#include <linux/srcu.h> + #define NOTIFY_DONE 0 #define NOTIFY_OK 0x0001 #define NOTIFY_STOP_MASK 0x8000 diff --git a/sys/compat/linuxkpi/common/include/linux/pci.h b/sys/compat/linuxkpi/common/include/linux/pci.h index 06336bf963d6..ccbd425de5da 100644 --- a/sys/compat/linuxkpi/common/include/linux/pci.h +++ b/sys/compat/linuxkpi/common/include/linux/pci.h @@ -76,24 +76,6 @@ struct pci_device_id { MODULE_PNP_INFO("U32:vendor;U32:device;V32:subvendor;V32:subdevice", \ _bus, lkpi_ ## _table, _table, nitems(_table) - 1) -/* Linux has an empty element at the end of the ID table -> nitems() - 1. */ -#define MODULE_DEVICE_TABLE(_bus, _table) \ - \ -static device_method_t _ ## _bus ## _ ## _table ## _methods[] = { \ - DEVMETHOD_END \ -}; \ - \ -static driver_t _ ## _bus ## _ ## _table ## _driver = { \ - "lkpi_" #_bus #_table, \ - _ ## _bus ## _ ## _table ## _methods, \ - 0 \ -}; \ - \ -DRIVER_MODULE(lkpi_ ## _table, _bus, _ ## _bus ## _ ## _table ## _driver,\ - 0, 0); \ - \ -MODULE_DEVICE_TABLE_BUS_ ## _bus(_bus, _table) - #define PCI_ANY_ID -1U #define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) @@ -253,6 +235,20 @@ extern const char *pci_power_names[6]; #define PCI_IRQ_LEGACY PCI_IRQ_INTX #endif +/* + * Linux PCI code uses `PCI_SET_ERROR_RESPONSE()` to indicate to the caller of + * a `pci_read_*()` function that the read failed. An example of failure is + * whether the device was disconnected. It is a bit weird because Linux + * `pci_read_*()` can return an error value, as the read value is stored in a + * integer passed by pointer. + * + * We don't set PCI_ERROR_RESPONSE anywhere as of this commit, but the DRM + * drivers started to use `PCI_POSSIBLE_ERROR()`. + */ +#define PCI_ERROR_RESPONSE (~0ULL) +#define PCI_SET_ERROR_RESPONSE(val) (*(val) = ((typeof(*(val))) PCI_ERROR_RESPONSE)) +#define PCI_POSSIBLE_ERROR(val) ((val) == ((typeof(val)) PCI_ERROR_RESPONSE)) + struct pci_dev; struct pci_driver { @@ -1122,19 +1118,28 @@ pci_num_vf(struct pci_dev *dev) static inline enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev) { + struct pci_dev *pbus; device_t root; uint32_t lnkcap, lnkcap2; int error, pos; - root = device_get_parent(dev->dev.bsddev); - if (root == NULL) - return (PCI_SPEED_UNKNOWN); - root = device_get_parent(root); - if (root == NULL) - return (PCI_SPEED_UNKNOWN); - root = device_get_parent(root); - if (root == NULL) - return (PCI_SPEED_UNKNOWN); + /* + * We should always be called on a PCI device. + * The only current consumer I could find was amdgpu which either + * calls us directly on a pdev(drmn?) or with the result of + * pci_upstream_bridge(). + * + * Treat "drmn" as special again as it is not a PCI device. + */ + if (dev->pdrv != NULL && dev->pdrv->isdrm) { + pbus = pci_upstream_bridge(dev); + if (pbus == NULL) + return (PCI_SPEED_UNKNOWN); + } else + pbus = dev; + + /* "root" may be misleading as it may not be that. */ + root = pbus->dev.bsddev; if (pci_get_vendor(root) == PCI_VENDOR_ID_VIA || pci_get_vendor(root) == PCI_VENDOR_ID_SERVERWORKS) diff --git a/sys/compat/linuxkpi/common/include/linux/platform_data/brcmfmac.h b/sys/compat/linuxkpi/common/include/linux/platform_data/brcmfmac.h new file mode 100644 index 000000000000..ec99b7b73d1d --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/platform_data/brcmfmac.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2016 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LINUX_BRCMFMAC_PLATFORM_H +#define _LINUX_BRCMFMAC_PLATFORM_H + + +#define BRCMFMAC_PDATA_NAME "brcmfmac" + +#define BRCMFMAC_COUNTRY_BUF_SZ 4 + + +/* + * Platform specific driver functions and data. Through the platform specific + * device data functions and data can be provided to help the brcmfmac driver to + * operate with the device in combination with the used platform. + */ + + +/** + * Note: the brcmfmac can be loaded as module or be statically built-in into + * the kernel. If built-in then do note that it uses module_init (and + * module_exit) routines which equal device_initcall. So if you intend to + * create a module with the platform specific data for the brcmfmac and have + * it built-in to the kernel then use a higher initcall then device_initcall + * (see init.h). If this is not done then brcmfmac will load without problems + * but will not pickup the platform data. + * + * When the driver does not "detect" platform driver data then it will continue + * without reporting anything and just assume there is no data needed. Which is + * probably true for most platforms. + */ + +/** + * enum brcmf_bus_type - Bus type identifier. Currently SDIO, USB and PCIE are + * supported. + */ +enum brcmf_bus_type { + BRCMF_BUSTYPE_SDIO, + BRCMF_BUSTYPE_USB, + BRCMF_BUSTYPE_PCIE +}; + + +/** + * struct brcmfmac_sdio_pd - SDIO Device specific platform data. + * + * @txglomsz: SDIO txglom size. Use 0 if default of driver is to be + * used. + * @drive_strength: is the preferred drive_strength to be used for the SDIO + * pins. If 0 then a default value will be used. This is + * the target drive strength, the exact drive strength + * which will be used depends on the capabilities of the + * device. + * @oob_irq_supported: does the board have support for OOB interrupts. SDIO + * in-band interrupts are relatively slow and for having + * less overhead on interrupt processing an out of band + * interrupt can be used. If the HW supports this then + * enable this by setting this field to true and configure + * the oob related fields. + * @oob_irq_nr, + * @oob_irq_flags: the OOB interrupt information. The values are used for + * registering the irq using request_irq function. + * @broken_sg_support: flag for broken sg list support of SDIO host controller. + * Set this to true if the SDIO host controller has higher + * align requirement than 32 bytes for each scatterlist + * item. + * @sd_head_align: alignment requirement for start of data buffer. + * @sd_sgentry_align: length alignment requirement for each sg entry. + * @reset: This function can get called if the device communication + * broke down. This functionality is particularly useful in + * case of SDIO type devices. It is possible to reset a + * dongle via sdio data interface, but it requires that + * this is fully functional. This function is chip/module + * specific and this function should return only after the + * complete reset has completed. + */ +struct brcmfmac_sdio_pd { + int txglomsz; + unsigned int drive_strength; + bool oob_irq_supported; + unsigned int oob_irq_nr; + unsigned long oob_irq_flags; + bool broken_sg_support; + unsigned short sd_head_align; + unsigned short sd_sgentry_align; + void (*reset)(void); +}; + +/** + * struct brcmfmac_pd_cc_entry - Struct for translating user space country code + * (iso3166) to firmware country code and + * revision. + * + * @iso3166: iso3166 alpha 2 country code string. + * @cc: firmware country code string. + * @rev: firmware country code revision. + */ +struct brcmfmac_pd_cc_entry { + char iso3166[BRCMFMAC_COUNTRY_BUF_SZ]; + char cc[BRCMFMAC_COUNTRY_BUF_SZ]; + s32 rev; +}; + +/** + * struct brcmfmac_pd_cc - Struct for translating country codes as set by user + * space to a country code and rev which can be used by + * firmware. + * + * @table_size: number of entries in table (> 0) + * @table: array of 1 or more elements with translation information. + */ +struct brcmfmac_pd_cc { + int table_size; + struct brcmfmac_pd_cc_entry table[]; +}; + +/** + * struct brcmfmac_pd_device - Device specific platform data. (id/rev/bus_type) + * is the unique identifier of the device. + * + * @id: ID of the device for which this data is. In case of SDIO + * or PCIE this is the chipid as identified by chip.c In + * case of USB this is the chipid as identified by the + * device query. + * @rev: chip revision, see id. + * @bus_type: The type of bus. Some chipid/rev exist for different bus + * types. Each bus type has its own set of settings. + * @feature_disable: Bitmask of features to disable (override), See feature.c + * in brcmfmac for details. + * @country_codes: If available, pointer to struct for translating country + * codes. + * @bus: Bus specific (union) device settings. Currently only + * SDIO. + */ +struct brcmfmac_pd_device { + unsigned int id; + unsigned int rev; + enum brcmf_bus_type bus_type; + unsigned int feature_disable; + struct brcmfmac_pd_cc *country_codes; + union { + struct brcmfmac_sdio_pd sdio; + } bus; +}; + +/** + * struct brcmfmac_platform_data - BRCMFMAC specific platform data. + * + * @power_on: This function is called by the brcmfmac driver when the module + * gets loaded. This can be particularly useful for low power + * devices. The platform spcific routine may for example decide to + * power up the complete device. If there is no use-case for this + * function then provide NULL. + * @power_off: This function is called by the brcmfmac when the module gets + * unloaded. At this point the devices can be powered down or + * otherwise be reset. So if an actual power_off is not supported + * but reset is supported by the devices then reset the devices + * when this function gets called. This can be particularly useful + * for low power devices. If there is no use-case for this + * function then provide NULL. + */ +struct brcmfmac_platform_data { + void (*power_on)(void); + void (*power_off)(void); + char *fw_alternative_path; + int device_count; + struct brcmfmac_pd_device devices[]; +}; + + +#endif /* _LINUX_BRCMFMAC_PLATFORM_H */ diff --git a/sys/compat/linuxkpi/common/include/linux/printk.h b/sys/compat/linuxkpi/common/include/linux/printk.h index d2d197682782..066ec900f04e 100644 --- a/sys/compat/linuxkpi/common/include/linux/printk.h +++ b/sys/compat/linuxkpi/common/include/linux/printk.h @@ -48,7 +48,13 @@ int __lkpi_hexdump_printf(void *, const char *, ...) __printflike(2, 3); void lkpi_hex_dump(int(*)(void *, const char *, ...), void *arg1, const char *, const char *, const int, const int, const int, - const void *, size_t, const bool); + const void *, size_t, const bool, const bool); + +#define hex_dump_to_buffer(buf, len, rowsize, groupsize, linebuf, linebuflen, ascii) \ + lkpi_hex_dump_to_buffer((buf), (len), (rowsize), (groupsize), (linebuf), (linebuflen), (ascii)) + +int lkpi_hex_dump_to_buffer(const void *buf, size_t len, int rowsize, + int groupsize, char *linebuf, size_t linebuflen, bool ascii); static inline void print_hex_dump(const char *level, const char *prefix_str, @@ -56,7 +62,7 @@ print_hex_dump(const char *level, const char *prefix_str, const void *buf, size_t len, const bool ascii) { lkpi_hex_dump(__lkpi_hexdump_printf, NULL, level, prefix_str, prefix_type, - rowsize, groupsize, buf, len, ascii); + rowsize, groupsize, buf, len, ascii, true); } static inline void diff --git a/sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h b/sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h index aad46cc25b1b..6491cbeab7e2 100644 --- a/sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h +++ b/sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h @@ -49,6 +49,7 @@ struct ptp_clock_info { int (*adjtime)(struct ptp_clock_info *, s64); int (*getcrosststamp)(struct ptp_clock_info *, struct system_device_crosststamp *); int (*gettime64)(struct ptp_clock_info *, struct timespec *); + int (*settime64)(struct ptp_clock_info *, const struct timespec *); }; static inline struct ptp_clock * diff --git a/sys/compat/linuxkpi/common/include/linux/radix-tree.h b/sys/compat/linuxkpi/common/include/linux/radix-tree.h index 1019697303db..7182f4a9e407 100644 --- a/sys/compat/linuxkpi/common/include/linux/radix-tree.h +++ b/sys/compat/linuxkpi/common/include/linux/radix-tree.h @@ -38,12 +38,19 @@ #define RADIX_TREE_MAX_HEIGHT \ howmany(sizeof(long) * NBBY, RADIX_TREE_MAP_SHIFT) +#define RADIX_TREE_MAX_TAGS 3 +#define RADIX_TREE_TAG_LONGS RADIX_TREE_MAP_SIZE + #define RADIX_TREE_ENTRY_MASK 3UL #define RADIX_TREE_EXCEPTIONAL_ENTRY 2UL #define RADIX_TREE_EXCEPTIONAL_SHIFT 2 +#define RADIX_TREE_ITER_TAG_MASK 0x0f +#define RADIX_TREE_ITER_TAGGED 0x10 + struct radix_tree_node { void *slots[RADIX_TREE_MAP_SIZE]; + unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS]; int count; }; @@ -51,6 +58,8 @@ struct radix_tree_root { struct radix_tree_node *rnode; gfp_t gfp_mask; int height; + /* Linux stores root tags inside `gfp_mask`. */ + unsigned long tags[RADIX_TREE_MAX_TAGS]; }; struct radix_tree_iter { @@ -64,9 +73,22 @@ struct radix_tree_iter { #define RADIX_TREE(name, mask) \ struct radix_tree_root name = RADIX_TREE_INIT(mask) -#define radix_tree_for_each_slot(slot, root, iter, start) \ - for ((iter)->index = (start); \ - radix_tree_iter_find(root, iter, &(slot)); (iter)->index++) +#define radix_tree_for_each_slot(slot, root, iter, start) \ + for ((iter)->index = (start); \ + radix_tree_iter_find(root, iter, &(slot), 0); \ + (iter)->index++) + +#define radix_tree_for_each_slot_tagged(slot, root, iter, start, tag) \ + for ((iter)->index = (start); \ + radix_tree_iter_find(root, iter, &(slot), \ + RADIX_TREE_ITER_TAGGED | tag); \ + (iter)->index++) + +static inline void * +radix_tree_deref_slot(void **slot) +{ + return (*slot); +} static inline int radix_tree_exception(void *arg) @@ -78,7 +100,12 @@ void *radix_tree_lookup(const struct radix_tree_root *, unsigned long); void *radix_tree_delete(struct radix_tree_root *, unsigned long); int radix_tree_insert(struct radix_tree_root *, unsigned long, void *); int radix_tree_store(struct radix_tree_root *, unsigned long, void **); -bool radix_tree_iter_find(const struct radix_tree_root *, struct radix_tree_iter *, void ***); +bool radix_tree_iter_find(const struct radix_tree_root *, struct radix_tree_iter *, void ***, int); void radix_tree_iter_delete(struct radix_tree_root *, struct radix_tree_iter *, void **); +void *radix_tree_tag_set(struct radix_tree_root *root, unsigned long index, unsigned int tag); +void *radix_tree_tag_clear(struct radix_tree_root *root, unsigned long index, unsigned int tag); +int radix_tree_tagged(const struct radix_tree_root *root, unsigned int tag); +unsigned int radix_tree_gang_lookup(const struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items); +unsigned int radix_tree_gang_lookup_tag(const struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items, unsigned int tag); #endif /* _LINUXKPI_LINUX_RADIX_TREE_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/ratelimit.h b/sys/compat/linuxkpi/common/include/linux/ratelimit.h index 9585b4b994d7..fdef57c7882d 100644 --- a/sys/compat/linuxkpi/common/include/linux/ratelimit.h +++ b/sys/compat/linuxkpi/common/include/linux/ratelimit.h @@ -14,4 +14,11 @@ struct ratelimit_state { #define ratelimit_state_init(x, y, z) #define ratelimit_set_flags(x, y) +#define WARN_RATELIMIT(condition, ...) ({ \ + bool __ret_warn_on = (condition); \ + if (unlikely(__ret_warn_on)) \ + pr_warn_ratelimited(__VA_ARGS__); \ + unlikely(__ret_warn_on); \ +}) + #endif diff --git a/sys/compat/linuxkpi/common/include/linux/regulator/consumer.h b/sys/compat/linuxkpi/common/include/linux/regulator/consumer.h new file mode 100644 index 000000000000..d6c23575bc83 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/regulator/consumer.h @@ -0,0 +1,17 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025-2026 The FreeBSD Foundation + * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org> + * + * This software was developed by Jean-Sébastien Pédron under sponsorship + * from the FreeBSD Foundation. + */ + +#ifndef _LINUXKPI_LINUX_REGULATOR_CONSUMER_H_ +#define _LINUXKPI_LINUX_REGULATOR_CONSUMER_H_ + +#include <linux/err.h> +#include <linux/suspend.h> + +#endif diff --git a/sys/compat/linuxkpi/common/include/linux/seq_buf.h b/sys/compat/linuxkpi/common/include/linux/seq_buf.h new file mode 100644 index 000000000000..d6246a40e6f7 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/seq_buf.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2025-2026 The FreeBSD Foundation + * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org> + * + * This software was developed by Jean-Sébastien Pédron under sponsorship + * from the FreeBSD Foundation. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef _LINUXKPI_LINUX_SEQ_BUF_H_ +#define _LINUXKPI_LINUX_SEQ_BUF_H_ + +#include <linux/bug.h> +#include <linux/minmax.h> +#include <linux/seq_file.h> +#include <linux/types.h> + +struct seq_buf { + char *buffer; + size_t size; + size_t len; +}; + +#define DECLARE_SEQ_BUF(NAME, SIZE) \ + struct seq_buf NAME = { \ + .buffer = (char[SIZE]) { 0 }, \ + .size = SIZE, \ + } + +static inline void +seq_buf_clear(struct seq_buf *s) +{ + s->len = 0; + if (s->size > 0) + s->buffer[0] = '\0'; +} + +static inline void +seq_buf_set_overflow(struct seq_buf *s) +{ + s->len = s->size + 1; +} + +static inline bool +seq_buf_has_overflowed(struct seq_buf *s) +{ + return (s->len > s->size); +} + +static inline bool +seq_buf_buffer_left(struct seq_buf *s) +{ + if (seq_buf_has_overflowed(s)) + return (0); + + return (s->size - s->len); +} + +#define seq_buf_init(s, buf, size) linuxkpi_seq_buf_init((s), (buf), (size)) +void linuxkpi_seq_buf_init(struct seq_buf *s, char *buf, unsigned int size); + +#define seq_buf_printf(s, f, ...) linuxkpi_seq_buf_printf((s), (f), __VA_ARGS__) +int linuxkpi_seq_buf_printf(struct seq_buf *s, const char *fmt, ...) \ + __printflike(2, 3); + +#define seq_buf_vprintf(s, f, a) linuxkpi_seq_buf_vprintf((s), (f), (a)) +int linuxkpi_seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args); + +#define seq_buf_str(s) linuxkpi_seq_buf_str((s)) +const char * linuxkpi_seq_buf_str(struct seq_buf *s); + +#endif diff --git a/sys/compat/linuxkpi/common/include/linux/seq_file.h b/sys/compat/linuxkpi/common/include/linux/seq_file.h index 47da16ab8688..786c09bd6a20 100644 --- a/sys/compat/linuxkpi/common/include/linux/seq_file.h +++ b/sys/compat/linuxkpi/common/include/linux/seq_file.h @@ -85,7 +85,7 @@ struct seq_operations { int (*show) (struct seq_file *m, void *v); }; -ssize_t seq_read(struct linux_file *, char *, size_t, off_t *); +ssize_t seq_read(struct linux_file *, char __user *, size_t, off_t *); int seq_write(struct seq_file *seq, const void *data, size_t len); void seq_putc(struct seq_file *m, char c); void seq_puts(struct seq_file *m, const char *str); @@ -115,7 +115,7 @@ seq_hex_dump(struct seq_file *m, const char *prefix_str, int prefix_type, int rowsize, int groupsize, const void *buf, size_t len, bool ascii) { lkpi_hex_dump(__lkpi_hexdump_sbuf_printf, m->buf, NULL, prefix_str, prefix_type, - rowsize, groupsize, buf, len, ascii); + rowsize, groupsize, buf, len, ascii, true); } #define file linux_file diff --git a/sys/compat/linuxkpi/common/include/linux/siphash.h b/sys/compat/linuxkpi/common/include/linux/siphash.h new file mode 100644 index 000000000000..9153e77382e1 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/siphash.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* Copyright (C) 2016-2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. + * + * SipHash: a fast short-input PRF + * https://131002.net/siphash/ + * + * This implementation is specifically for SipHash2-4 for a secure PRF + * and HalfSipHash1-3/SipHash1-3 for an insecure PRF only suitable for + * hashtables. + */ + +#ifndef _LINUX_SIPHASH_H +#define _LINUX_SIPHASH_H + +#include <linux/types.h> +#include <linux/kernel.h> + +#define SIPHASH_ALIGNMENT __alignof__(u64) +typedef struct { + u64 key[2]; +} siphash_key_t; + +#define siphash_aligned_key_t siphash_key_t __aligned(16) + +static inline bool siphash_key_is_zero(const siphash_key_t *key) +{ + return !(key->key[0] | key->key[1]); +} + +u64 __siphash_aligned(const void *data, size_t len, const siphash_key_t *key); +u64 __siphash_unaligned(const void *data, size_t len, const siphash_key_t *key); + +u64 siphash_1u64(const u64 a, const siphash_key_t *key); +u64 siphash_2u64(const u64 a, const u64 b, const siphash_key_t *key); +u64 siphash_3u64(const u64 a, const u64 b, const u64 c, + const siphash_key_t *key); +u64 siphash_4u64(const u64 a, const u64 b, const u64 c, const u64 d, + const siphash_key_t *key); +u64 siphash_1u32(const u32 a, const siphash_key_t *key); +u64 siphash_3u32(const u32 a, const u32 b, const u32 c, + const siphash_key_t *key); + +static inline u64 siphash_2u32(const u32 a, const u32 b, + const siphash_key_t *key) +{ + return siphash_1u64((u64)b << 32 | a, key); +} +static inline u64 siphash_4u32(const u32 a, const u32 b, const u32 c, + const u32 d, const siphash_key_t *key) +{ + return siphash_2u64((u64)b << 32 | a, (u64)d << 32 | c, key); +} + + +static inline u64 ___siphash_aligned(const __le64 *data, size_t len, + const siphash_key_t *key) +{ + if (__builtin_constant_p(len) && len == 4) + return siphash_1u32(le32_to_cpup((const __le32 *)data), key); + if (__builtin_constant_p(len) && len == 8) + return siphash_1u64(le64_to_cpu(data[0]), key); + if (__builtin_constant_p(len) && len == 16) + return siphash_2u64(le64_to_cpu(data[0]), le64_to_cpu(data[1]), + key); + if (__builtin_constant_p(len) && len == 24) + return siphash_3u64(le64_to_cpu(data[0]), le64_to_cpu(data[1]), + le64_to_cpu(data[2]), key); + if (__builtin_constant_p(len) && len == 32) + return siphash_4u64(le64_to_cpu(data[0]), le64_to_cpu(data[1]), + le64_to_cpu(data[2]), le64_to_cpu(data[3]), + key); + return __siphash_aligned(data, len, key); +} + +/** + * siphash - compute 64-bit siphash PRF value + * @data: buffer to hash + * @size: size of @data + * @key: the siphash key + */ +static inline u64 siphash(const void *data, size_t len, + const siphash_key_t *key) +{ + if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) || + !IS_ALIGNED((unsigned long)data, SIPHASH_ALIGNMENT)) + return __siphash_unaligned(data, len, key); + return ___siphash_aligned(data, len, key); +} + +#define HSIPHASH_ALIGNMENT __alignof__(unsigned long) +typedef struct { + unsigned long key[2]; +} hsiphash_key_t; + +u32 __hsiphash_aligned(const void *data, size_t len, + const hsiphash_key_t *key); +u32 __hsiphash_unaligned(const void *data, size_t len, + const hsiphash_key_t *key); + +u32 hsiphash_1u32(const u32 a, const hsiphash_key_t *key); +u32 hsiphash_2u32(const u32 a, const u32 b, const hsiphash_key_t *key); +u32 hsiphash_3u32(const u32 a, const u32 b, const u32 c, + const hsiphash_key_t *key); +u32 hsiphash_4u32(const u32 a, const u32 b, const u32 c, const u32 d, + const hsiphash_key_t *key); + +static inline u32 ___hsiphash_aligned(const __le32 *data, size_t len, + const hsiphash_key_t *key) +{ + if (__builtin_constant_p(len) && len == 4) + return hsiphash_1u32(le32_to_cpu(data[0]), key); + if (__builtin_constant_p(len) && len == 8) + return hsiphash_2u32(le32_to_cpu(data[0]), le32_to_cpu(data[1]), + key); + if (__builtin_constant_p(len) && len == 12) + return hsiphash_3u32(le32_to_cpu(data[0]), le32_to_cpu(data[1]), + le32_to_cpu(data[2]), key); + if (__builtin_constant_p(len) && len == 16) + return hsiphash_4u32(le32_to_cpu(data[0]), le32_to_cpu(data[1]), + le32_to_cpu(data[2]), le32_to_cpu(data[3]), + key); + return __hsiphash_aligned(data, len, key); +} + +/** + * hsiphash - compute 32-bit hsiphash PRF value + * @data: buffer to hash + * @size: size of @data + * @key: the hsiphash key + */ +static inline u32 hsiphash(const void *data, size_t len, + const hsiphash_key_t *key) +{ + if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) || + !IS_ALIGNED((unsigned long)data, HSIPHASH_ALIGNMENT)) + return __hsiphash_unaligned(data, len, key); + return ___hsiphash_aligned(data, len, key); +} + +/* + * These macros expose the raw SipHash and HalfSipHash permutations. + * Do not use them directly! If you think you have a use for them, + * be sure to CC the maintainer of this file explaining why. + */ + +#define SIPHASH_PERMUTATION(a, b, c, d) ( \ + (a) += (b), (b) = rol64((b), 13), (b) ^= (a), (a) = rol64((a), 32), \ + (c) += (d), (d) = rol64((d), 16), (d) ^= (c), \ + (a) += (d), (d) = rol64((d), 21), (d) ^= (a), \ + (c) += (b), (b) = rol64((b), 17), (b) ^= (c), (c) = rol64((c), 32)) + +#define SIPHASH_CONST_0 0x736f6d6570736575ULL +#define SIPHASH_CONST_1 0x646f72616e646f6dULL +#define SIPHASH_CONST_2 0x6c7967656e657261ULL +#define SIPHASH_CONST_3 0x7465646279746573ULL + +#define HSIPHASH_PERMUTATION(a, b, c, d) ( \ + (a) += (b), (b) = rol32((b), 5), (b) ^= (a), (a) = rol32((a), 16), \ + (c) += (d), (d) = rol32((d), 8), (d) ^= (c), \ + (a) += (d), (d) = rol32((d), 7), (d) ^= (a), \ + (c) += (b), (b) = rol32((b), 13), (b) ^= (c), (c) = rol32((c), 16)) + +#define HSIPHASH_CONST_0 0U +#define HSIPHASH_CONST_1 0U +#define HSIPHASH_CONST_2 0x6c796765U +#define HSIPHASH_CONST_3 0x74656462U + +#endif /* _LINUX_SIPHASH_H */ diff --git a/sys/compat/linuxkpi/common/include/linux/skbuff.h b/sys/compat/linuxkpi/common/include/linux/skbuff.h index 2e560a120e41..c43d6daff5ee 100644 --- a/sys/compat/linuxkpi/common/include/linux/skbuff.h +++ b/sys/compat/linuxkpi/common/include/linux/skbuff.h @@ -770,7 +770,7 @@ ___skb_queue_splice(const struct sk_buff_head *from, } static inline void -skb_queue_splice_init(struct sk_buff_head *from, struct sk_buff_head *to) +skb_queue_splice(const struct sk_buff_head *from, struct sk_buff_head *to) { SKB_TRACE2(from, to); @@ -780,6 +780,13 @@ skb_queue_splice_init(struct sk_buff_head *from, struct sk_buff_head *to) ___skb_queue_splice(from, (struct sk_buff *)to, to->next); to->qlen += from->qlen; +} + +static inline void +skb_queue_splice_init(struct sk_buff_head *from, struct sk_buff_head *to) +{ + + skb_queue_splice(from, to); __skb_queue_head_init(from); } diff --git a/sys/compat/linuxkpi/common/include/linux/soc/airoha/airoha_offload.h b/sys/compat/linuxkpi/common/include/linux/soc/airoha/airoha_offload.h new file mode 100644 index 000000000000..ade0b06d839f --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/soc/airoha/airoha_offload.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2026 Bjoern A. Zeeb + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef _LINUXKPI_LINUX_SOC_AIROHA_AIROHA_OFFLOAD_H +#define _LINUXKPI_LINUX_SOC_AIROHA_AIROHA_OFFLOAD_H + +#include <linux/kernel.h> /* pr_debug */ + +enum airoha_npu_wlan_get_cmd { + __dummy_airoha_npu_wlan_get_cmd, +}; +enum airoha_npu_wlan_set_cmd { + __dummy_airoha_npu_wlan_set_cmd, +}; + +struct airoha_npu { +}; +struct airoha_npu_rx_dma_desc { +}; +struct airoha_npu_tx_dma_desc { +}; + +static __inline int +airoha_npu_wlan_send_msg(void *npu, int ifindex, + enum airoha_npu_wlan_set_cmd cmd, void *val, size_t len, gfp_t gfp) +{ + pr_debug("%s: TODO\n", __func__); + return (-EOPNOTSUPP); +} + +static __inline int +airoha_npu_wlan_get_msg(void *npu, int ifindex, + enum airoha_npu_wlan_get_cmd cmd, void *val, size_t len, gfp_t gfp) +{ + pr_debug("%s: TODO\n", __func__); + return (-EOPNOTSUPP); +} + +static __inline void +airoha_npu_wlan_enable_irq(struct airoha_npu *npu, int q) +{ + pr_debug("%s: TODO\n", __func__); +} + +#endif /* _LINUXKPI_LINUX_SOC_AIROHA_AIROHA_OFFLOAD_H */ diff --git a/sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h b/sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h index 2b9c6ae4911e..64daa8c78c9d 100644 --- a/sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h +++ b/sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2022-2025 Bjoern A. Zeeb + * Copyright (c) 2022-2026 Bjoern A. Zeeb * * SPDX-License-Identifier: BSD-2-Clause */ @@ -37,7 +37,13 @@ mtk_wed_device_active(struct mtk_wed_device *dev __unused) static inline bool mtk_wed_get_rx_capa(struct mtk_wed_device *dev __unused) { + pr_debug("%s: TODO\n", __func__); + return (false); +} +static inline bool +mtk_wed_is_amsdu_supported(struct mtk_wed_device *dev __unused) +{ pr_debug("%s: TODO\n", __func__); return (false); } @@ -66,6 +72,12 @@ mtk_wed_get_rx_capa(struct mtk_wed_device *dev __unused) { return (false); } + +static inline bool +mtk_wed_is_amsdu_supported(struct mtk_wed_device *dev __unused) +{ + return (false); +} #endif /* CONFIG_NET_MEDIATEK_SOC_WED */ #endif /* _LINUXKPI_LINUX_SOC_MEDIATEK_MTK_WED_H */ diff --git a/sys/compat/linuxkpi/common/include/linux/sort.h b/sys/compat/linuxkpi/common/include/linux/sort.h index e6196d1f41c7..361b37c587c8 100644 --- a/sys/compat/linuxkpi/common/include/linux/sort.h +++ b/sys/compat/linuxkpi/common/include/linux/sort.h @@ -34,7 +34,7 @@ #include <sys/libkern.h> #define sort(base, num, size, cmp, swap) do { \ - BUILD_BUG_ON_ZERO(swap); \ + BUILD_BUG_ON(swap); \ qsort(base, num, size, cmp); \ } while (0) diff --git a/sys/compat/linuxkpi/common/include/linux/spinlock.h b/sys/compat/linuxkpi/common/include/linux/spinlock.h index dc10b0457153..a786cbab5e13 100644 --- a/sys/compat/linuxkpi/common/include/linux/spinlock.h +++ b/sys/compat/linuxkpi/common/include/linux/spinlock.h @@ -36,10 +36,12 @@ #include <sys/mutex.h> #include <sys/kdb.h> +#include <linux/cleanup.h> #include <linux/compiler.h> #include <linux/rwlock.h> #include <linux/bottom_half.h> #include <linux/lockdep.h> +#include <linux/preempt.h> typedef struct mtx spinlock_t; @@ -178,4 +180,33 @@ _atomic_dec_and_lock_irqsave(atomic_t *cnt, spinlock_t *lock, return (0); } +/* + * struct raw_spinlock + */ + +typedef struct raw_spinlock { + struct mtx lock; +} raw_spinlock_t; + +#define raw_spin_lock_init(rlock) \ + mtx_init(&(rlock)->lock, spin_lock_name("lnxspin_raw"), \ + NULL, MTX_DEF | MTX_NOWITNESS | MTX_NEW) + +#define raw_spin_lock(rl) spin_lock(&(rl)->lock) +#define raw_spin_trylock(rl) spin_trylock(&(rl)->lock) +#define raw_spin_unlock(rl) spin_unlock(&(rl)->lock) + +#define raw_spin_lock_irqsave(rl, f) spin_lock_irqsave(&(rl)->lock, (f)) +#define raw_spin_trylock_irqsave(rl, f) spin_trylock_irqsave(&(rl)->lock, (f)) +#define raw_spin_unlock_irqrestore(rl, f) spin_unlock_irqrestore(&(rl)->lock, (f)) + +/* + * cleanup.h related pre-defined cases. + */ +DEFINE_LOCK_GUARD_1(spinlock_irqsave, + spinlock_t, + spin_lock_irqsave(_T->lock, _T->flags), + spin_unlock_irqrestore(_T->lock, _T->flags), + unsigned long flags) + #endif /* _LINUXKPI_LINUX_SPINLOCK_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/ssb/ssb_regs.h b/sys/compat/linuxkpi/common/include/linux/ssb/ssb_regs.h new file mode 100644 index 000000000000..e1c18b6b632a --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/ssb/ssb_regs.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 Bjoern A. Zeeb + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef _LINUXKPI_LINUX_SSB_SSB_REGS_H +#define _LINUXKPI_LINUX_SSB_SSB_REGS_H + +#define SSB_IDHIGH_RCHI 0x00007000 +#define SSB_IDHIGH_RCHI_SHIFT 8 +#define SSB_IDHIGH_RCLO 0x0000000F +#define SSB_IDLOW_INITIATOR 0x00000080 +#define SSB_IMSTATE_BUSY 0x01800000 +#define SSB_IMSTATE_IBE 0x00020000 +#define SSB_IMSTATE_REJECT 0x02000000 +#define SSB_IMSTATE_TO 0x00040000 +#define SSB_TMSHIGH_BUSY 0x00000004 +#define SSB_TMSHIGH_SERR 0x00000001 +#define SSB_TMSLOW_CLOCK 0x00010000 +#define SSB_TMSLOW_FGC 0x00020000 +#define SSB_TMSLOW_REJECT 0x00000002 +#define SSB_TMSLOW_RESET 0x00000001 + +#endif /* _LINUXKPI_LINUX_SSB_SSB_REGS_H */ diff --git a/sys/compat/linuxkpi/common/include/linux/string_choices.h b/sys/compat/linuxkpi/common/include/linux/string_choices.h index 74aa3fd019b2..db540d3e7d40 100644 --- a/sys/compat/linuxkpi/common/include/linux/string_choices.h +++ b/sys/compat/linuxkpi/common/include/linux/string_choices.h @@ -33,39 +33,33 @@ static inline const char * str_yes_no(bool value) { - if (value) - return "yes"; - else - return "no"; + return (value ? "yes" : "no"); } static inline const char * str_on_off(bool value) { - if (value) - return "on"; - else - return "off"; + return (value ? "on" : "off"); } static inline const char * str_enabled_disabled(bool value) { - if (value) - return "enabled"; - else - return "disabled"; + return (value ? "enabled" : "disabled"); } static inline const char * str_enable_disable(bool value) { - if (value) - return "enable"; - else - return "disable"; + return (value ? "enable" : "disable"); } #define str_disable_enable(_v) str_enable_disable(!(_v)) +static inline const char * +str_read_write(bool value) +{ + return (value ? "read" : "write"); +} + #endif diff --git a/sys/compat/linuxkpi/common/include/linux/suspend.h b/sys/compat/linuxkpi/common/include/linux/suspend.h index dacecbebdc08..3d5d5d594127 100644 --- a/sys/compat/linuxkpi/common/include/linux/suspend.h +++ b/sys/compat/linuxkpi/common/include/linux/suspend.h @@ -3,6 +3,12 @@ #ifndef _LINUXKPI_LINUX_SUSPEND_H_ #define _LINUXKPI_LINUX_SUSPEND_H_ +#include <linux/swap.h> +#include <linux/notifier.h> +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/mm.h> + typedef int suspend_state_t; extern suspend_state_t pm_suspend_target_state; diff --git a/sys/compat/linuxkpi/common/include/linux/swap.h b/sys/compat/linuxkpi/common/include/linux/swap.h index 5828db7ae392..9c1db9677f9e 100644 --- a/sys/compat/linuxkpi/common/include/linux/swap.h +++ b/sys/compat/linuxkpi/common/include/linux/swap.h @@ -37,7 +37,14 @@ #include <vm/swap_pager.h> #include <vm/vm_pageout.h> +#include <linux/spinlock.h> +#include <linux/mmzone.h> +#include <linux/list.h> +#include <linux/memcontrol.h> +#include <linux/sched.h> +#include <linux/fs.h> #include <linux/pagemap.h> +#include <linux/atomic.h> #include <linux/page-flags.h> static inline long diff --git a/sys/compat/linuxkpi/common/include/linux/sysfs.h b/sys/compat/linuxkpi/common/include/linux/sysfs.h index 470c224a9778..7c8c4e2e32b9 100644 --- a/sys/compat/linuxkpi/common/include/linux/sysfs.h +++ b/sys/compat/linuxkpi/common/include/linux/sysfs.h @@ -43,13 +43,6 @@ struct sysfs_ops { size_t); }; -struct attribute_group { - const char *name; - mode_t (*is_visible)(struct kobject *, - struct attribute *, int); - struct attribute **attrs; -}; - struct bin_attribute { struct attribute attr; size_t size; @@ -59,6 +52,14 @@ struct bin_attribute { struct bin_attribute *, char *, loff_t, size_t); }; +struct attribute_group { + const char *name; + mode_t (*is_visible)(struct kobject *, + struct attribute *, int); + struct attribute **attrs; + struct bin_attribute **bin_attrs; +}; + #define __ATTR(_name, _mode, _show, _store) { \ .attr = { .name = __stringify(_name), .mode = _mode }, \ .show = _show, .store = _store, \ @@ -370,6 +371,7 @@ static inline int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) { struct attribute **attr; + struct bin_attribute **bin_attr; struct sysctl_oid *oidp; /* Don't create the group node if grp->name is undefined. */ @@ -378,11 +380,19 @@ sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) OID_AUTO, grp->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, grp->name); else oidp = kobj->oidp; - for (attr = grp->attrs; *attr != NULL; attr++) { + for (attr = grp->attrs; attr != NULL && *attr != NULL; attr++) { SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO, (*attr)->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE, kobj, (uintptr_t)*attr, sysctl_handle_attr, "A", ""); } + for (bin_attr = grp->bin_attrs; + bin_attr != NULL && *bin_attr != NULL; + bin_attr++) { + SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO, + (*bin_attr)->attr.name, + CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_MPSAFE, + kobj, (uintptr_t)*bin_attr, sysctl_handle_bin_attr, "", ""); + } return (0); } @@ -434,14 +444,20 @@ static inline void sysfs_unmerge_group(struct kobject *kobj, const struct attribute_group *grp) { struct attribute **attr; + struct bin_attribute **bin_attr; struct sysctl_oid *oidp; SYSCTL_FOREACH(oidp, SYSCTL_CHILDREN(kobj->oidp)) { if (strcmp(oidp->oid_name, grp->name) != 0) continue; - for (attr = grp->attrs; *attr != NULL; attr++) { + for (attr = grp->attrs; attr != NULL && *attr != NULL; attr++) { sysctl_remove_name(oidp, (*attr)->name, 1, 1); } + for (bin_attr = grp->bin_attrs; + bin_attr != NULL && *bin_attr != NULL; + bin_attr++) { + sysctl_remove_name(oidp, (*bin_attr)->attr.name, 1, 1); + } } } diff --git a/sys/compat/linuxkpi/common/include/media/cec-notifier.h b/sys/compat/linuxkpi/common/include/media/cec-notifier.h new file mode 100644 index 000000000000..8d6fc452b907 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/media/cec-notifier.h @@ -0,0 +1,17 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025-2026 The FreeBSD Foundation + * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org> + * + * This software was developed by Jean-Sébastien Pédron under sponsorship + * from the FreeBSD Foundation. + */ + +#ifndef _LINUXKPI_MEDIA_CEC_NOTIFIER_H_ +#define _LINUXKPI_MEDIA_CEC_NOTIFIER_H_ + +#include <linux/err.h> +#include <media/cec.h> + +#endif diff --git a/sys/compat/linuxkpi/common/include/media/cec.h b/sys/compat/linuxkpi/common/include/media/cec.h new file mode 100644 index 000000000000..c96b57868e23 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/media/cec.h @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025-2026 The FreeBSD Foundation + * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org> + * + * This software was developed by Jean-Sébastien Pédron under sponsorship + * from the FreeBSD Foundation. + */ + +#ifndef _LINUXKPI_MEDIA_CEC_H_ +#define _LINUXKPI_MEDIA_CEC_H_ + +#include <linux/poll.h> +#include <linux/fs.h> +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/cdev.h> +#include <linux/kthread.h> +#include <linux/timer.h> +#include <linux/cec-funcs.h> + +#endif diff --git a/sys/compat/linuxkpi/common/include/net/cfg80211.h b/sys/compat/linuxkpi/common/include/net/cfg80211.h index d7ed2bc97c98..5c8c914bdee7 100644 --- a/sys/compat/linuxkpi/common/include/net/cfg80211.h +++ b/sys/compat/linuxkpi/common/include/net/cfg80211.h @@ -124,6 +124,7 @@ enum ieee80211_channel_flags { IEEE80211_CHAN_PSD = BIT(12), IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP = BIT(13), IEEE80211_CHAN_CAN_MONITOR = BIT(14), + IEEE80211_CHAN_NO_EHT = BIT(15), }; #define IEEE80211_CHAN_NO_HT40 (IEEE80211_CHAN_NO_HT40MINUS|IEEE80211_CHAN_NO_HT40PLUS) @@ -152,6 +153,8 @@ struct linuxkpi_ieee80211_channel { int orig_mpwr; }; +#define NL80211_EHT_NSS_MAX 16 + struct cfg80211_bitrate_mask { /* TODO FIXME */ struct { @@ -159,6 +162,7 @@ struct cfg80211_bitrate_mask { uint8_t ht_mcs[IEEE80211_HT_MCS_MASK_LEN]; uint16_t vht_mcs[8]; uint16_t he_mcs[8]; + uint16_t eht_mcs[NL80211_EHT_NSS_MAX]; enum nl80211_txrate_gi gi; enum nl80211_he_gi he_gi; uint8_t he_ltf; /* XXX enum? */ @@ -1017,6 +1021,14 @@ struct survey_info { /* net80211::struct ieee80211_channel_survey */ struct linuxkpi_ieee80211_channel *channel; }; +enum wiphy_bss_param_flags { + WIPHY_BSS_PARAM_AP_ISOLATE = BIT(0), +}; + +struct bss_parameters { + int ap_isolate; +}; + enum wiphy_vendor_cmd_need_flags { WIPHY_VENDOR_CMD_NEED_NETDEV = 0x01, WIPHY_VENDOR_CMD_NEED_RUNNING = 0x02, @@ -1138,6 +1150,8 @@ struct wiphy { int n_radio; const struct wiphy_radio *radio; + uint32_t bss_param_support; /* enum wiphy_bss_param_flags */ + int features, hw_version; int interface_modes, max_match_sets, max_remain_on_channel_duration, max_scan_ssids, max_sched_scan_ie_len, max_sched_scan_plan_interval, max_sched_scan_plan_iterations, max_sched_scan_plans, max_sched_scan_reqs, max_sched_scan_ssids; int num_iftype_ext_capab; @@ -1220,7 +1234,7 @@ struct cfg80211_ops { int (*dump_survey)(struct wiphy *, struct net_device *, int, struct survey_info *); int (*external_auth)(struct wiphy *, struct net_device *, struct cfg80211_external_auth_params *); int (*set_cqm_rssi_range_config)(struct wiphy *, struct net_device *, int, int); - + int (*change_bss)(struct wiphy *, struct net_device *, struct bss_parameters *); }; @@ -1230,6 +1244,7 @@ struct cfg80211_ops { struct wiphy *linuxkpi_wiphy_new(const struct cfg80211_ops *, size_t); void linuxkpi_wiphy_free(struct wiphy *wiphy); +int linuxkpi_80211_wiphy_register(struct wiphy *); void linuxkpi_wiphy_work_queue(struct wiphy *, struct wiphy_work *); void linuxkpi_wiphy_work_cancel(struct wiphy *, struct wiphy_work *); @@ -1749,8 +1764,7 @@ wiphy_net(struct wiphy *wiphy) static __inline int wiphy_register(struct wiphy *wiphy) { - TODO(); - return (0); + return (linuxkpi_80211_wiphy_register(wiphy)); } static __inline void diff --git a/sys/compat/linuxkpi/common/include/net/mac80211.h b/sys/compat/linuxkpi/common/include/net/mac80211.h index 6e2f3f2d8781..4f3aad532810 100644 --- a/sys/compat/linuxkpi/common/include/net/mac80211.h +++ b/sys/compat/linuxkpi/common/include/net/mac80211.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2020-2025 The FreeBSD Foundation + * Copyright (c) 2020-2026 The FreeBSD Foundation * Copyright (c) 2020-2025 Bjoern A. Zeeb * * This software was developed by Björn Zeeb under sponsorship from @@ -431,7 +431,6 @@ enum ieee80211_hw_flags { IEEE80211_HW_BUFF_MMPDU_TXQ, IEEE80211_HW_CHANCTX_STA_CSA, IEEE80211_HW_CONNECTION_MONITOR, - IEEE80211_HW_DEAUTH_NEED_MGD_TX_PREP, IEEE80211_HW_HAS_RATE_CONTROL, IEEE80211_HW_MFP_CAPABLE, IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR, @@ -789,10 +788,21 @@ struct ieee80211_tx_queue_params { struct ieee80211_he_mu_edca_param_ac_rec mu_edca_param_rec; }; +enum mac80211_rate_control_flags { + IEEE80211_TX_RC_40_MHZ_WIDTH = BIT(0), + IEEE80211_TX_RC_80_MHZ_WIDTH = BIT(1), + IEEE80211_TX_RC_160_MHZ_WIDTH = BIT(2), + IEEE80211_TX_RC_GREEN_FIELD = BIT(3), + IEEE80211_TX_RC_MCS = BIT(4), + IEEE80211_TX_RC_SHORT_GI = BIT(5), + IEEE80211_TX_RC_VHT_MCS = BIT(6), + IEEE80211_TX_RC_USE_SHORT_PREAMBLE = BIT(7), +}; + struct ieee80211_tx_rate { uint8_t idx; uint16_t count:5, - flags:11; + flags:11; /* enum mac80211_rate_control_flags */ }; enum ieee80211_vif_driver_flags { @@ -1092,6 +1102,8 @@ struct ieee80211_ops { void (*rfkill_poll)(struct ieee80211_hw *); + int (*net_fill_forward_path)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, struct net_device_path_ctx *, struct net_device_path *); + /* #ifdef CONFIG_MAC80211_DEBUGFS */ /* Do not change depending on compile-time option. */ void (*sta_add_debugfs)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, struct dentry *); void (*vif_add_debugfs)(struct ieee80211_hw *, struct ieee80211_vif *); diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c index 02724433d89d..01347586ef63 100644 --- a/sys/compat/linuxkpi/common/src/linux_80211.c +++ b/sys/compat/linuxkpi/common/src/linux_80211.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2020-2025 The FreeBSD Foundation + * Copyright (c) 2020-2026 The FreeBSD Foundation * Copyright (c) 2020-2025 Bjoern A. Zeeb * * This software was developed by Björn Zeeb under sponsorship from @@ -282,7 +282,7 @@ lkpi_nl80211_sta_info_to_str(struct sbuf *s, const char *prefix, } static void -lkpi_80211_dump_lvif_stas(struct lkpi_vif *lvif, struct sbuf *s) +lkpi_80211_dump_lvif_stas(struct lkpi_vif *lvif, struct sbuf *s, bool dump_queues) { struct lkpi_hw *lhw; struct ieee80211_hw *hw; @@ -292,6 +292,7 @@ lkpi_80211_dump_lvif_stas(struct lkpi_vif *lvif, struct sbuf *s) struct ieee80211_sta *sta; struct station_info sinfo; int error; + uint8_t tid; vif = LVIF_TO_VIF(lvif); vap = LVIF_TO_VAP(lvif); @@ -376,6 +377,39 @@ lkpi_80211_dump_lvif_stas(struct lkpi_vif *lvif, struct sbuf *s) sbuf_printf(s, " he_dcm %u he_gi %u he_ru_alloc %u eht_gi %u\n", sinfo.txrate.he_dcm, sinfo.txrate.he_gi, sinfo.txrate.he_ru_alloc, sinfo.txrate.eht_gi); + + if (!dump_queues) + continue; + + /* Dump queue information. */ + sbuf_printf(s, " Queue information:\n"); + sbuf_printf(s, " frms direct tx %ju\n", lsta->frms_tx); + for (tid = 0; tid <= IEEE80211_NUM_TIDS; tid++) { + struct lkpi_txq *ltxq; + + if (sta->txq[tid] == NULL) { + sbuf_printf(s, " tid %-2u NOQ\n", tid); + continue; + } + + ltxq = TXQ_TO_LTXQ(sta->txq[tid]); +#ifdef __notyet__ + sbuf_printf(s, " tid %-2u flags: %b " + "txq_generation %u skbq len %d\n", + tid, ltxq->flags, LKPI_TXQ_FLAGS_BITS, + ltxq->txq_generation, + skb_queue_len_lockless(<xq->skbq)); +#else + sbuf_printf(s, " tid %-2u " + "txq_generation %u skbq len %d\n", + tid, + ltxq->txq_generation, + skb_queue_len_lockless(<xq->skbq)); +#endif + sbuf_printf(s, " frms_enqueued %ju frms_dequeued %ju " + "frms_tx %ju\n", + ltxq->frms_enqueued, ltxq->frms_dequeued, ltxq->frms_tx); + } } wiphy_unlock(hw->wiphy); } @@ -393,7 +427,28 @@ lkpi_80211_dump_stas(SYSCTL_HANDLER_ARGS) sbuf_new_for_sysctl(&s, NULL, 1024, req); - lkpi_80211_dump_lvif_stas(lvif, &s); + lkpi_80211_dump_lvif_stas(lvif, &s, false); + + sbuf_finish(&s); + sbuf_delete(&s); + + return (0); +} + +static int +lkpi_80211_dump_sta_queues(SYSCTL_HANDLER_ARGS) +{ + struct lkpi_vif *lvif; + struct sbuf s; + + if (req->newptr) + return (EPERM); + + lvif = (struct lkpi_vif *)arg1; + + sbuf_new_for_sysctl(&s, NULL, 1024, req); + + lkpi_80211_dump_lvif_stas(lvif, &s, true); sbuf_finish(&s); sbuf_delete(&s); @@ -638,7 +693,8 @@ skip_bw: sta = IEEE80211_VHT_MCS_NOT_SUPPORTED; else { sta = MIN(sta, card); - rx_nss = i + 1; + if (rx_nss == 0) + rx_nss = i + 1; } } rx_map |= (sta << (2 * i)); @@ -840,41 +896,34 @@ lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN], /* Deflink information. */ for (band = 0; band < NUM_NL80211_BANDS; band++) { struct ieee80211_supported_band *supband; + uint32_t rate_mandatory;; supband = hw->wiphy->bands[band]; if (supband == NULL) continue; + switch (band) { + case NL80211_BAND_2GHZ: + /* We have to assume 11g support here. */ + rate_mandatory = IEEE80211_RATE_MANDATORY_G | + IEEE80211_RATE_MANDATORY_B; + break; + case NL80211_BAND_5GHZ: + rate_mandatory = IEEE80211_RATE_MANDATORY_A; + break; + default: + continue; + } + for (i = 0; i < supband->n_bitrates; i++) { - switch (band) { - case NL80211_BAND_2GHZ: - switch (supband->bitrates[i].bitrate) { - case 240: /* 11g only */ - case 120: /* 11g only */ - case 110: - case 60: /* 11g only */ - case 55: - case 20: - case 10: - sta->deflink.supp_rates[band] |= BIT(i); - break; - } - break; - case NL80211_BAND_5GHZ: - switch (supband->bitrates[i].bitrate) { - case 240: - case 120: - case 60: - sta->deflink.supp_rates[band] |= BIT(i); - break; - } - break; - } + if ((supband->bitrates[i].flags & rate_mandatory) != 0) + sta->deflink.supp_rates[band] |= BIT(i); } } sta->deflink.smps_mode = IEEE80211_SMPS_OFF; sta->deflink.bandwidth = IEEE80211_STA_RX_BW_20; + sta->deflink.agg.max_rc_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA; sta->deflink.rx_nss = 1; sta->deflink.sta = sta; @@ -1324,6 +1373,15 @@ lkpi_sta_del_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif, return (0); lockdep_assert_wiphy(hw->wiphy); + + if (vif->cfg.assoc && lsta->state == IEEE80211_STA_AUTHORIZED) { + if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO) + ic_printf(lsta->ni->ni_ic, + "%d %lu %s: vif still assoc; not deleting keys\n", + curthread->td_tid, jiffies, __func__); + return (0); + } + ieee80211_ref_node(lsta->ni); error = 0; @@ -1403,6 +1461,15 @@ lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) */ lockdep_assert_wiphy(hw->wiphy); + ni = ieee80211_ref_node(vap->iv_bss); + lsta = ni->ni_drv_data; + if (lsta == NULL) { + ic_printf(ic, "%s: ni %p (%6D) with lsta NULL\n", + __func__, ni, ni->ni_bssid, ":"); + ieee80211_free_node(ni); + return (0); + } + /* * While we are assoc we may still send packets. We cannot delete the * keys as otherwise packets could go out unencrypted. Some firmware @@ -1413,30 +1480,24 @@ lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) * How to test: run 800Mbit/s UDP traffic and during that restart your * supplicant. You want to survive that. */ - if (vif->cfg.assoc) { + if (vif->cfg.assoc && lsta->state == IEEE80211_STA_AUTHORIZED) { if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO) ic_printf(ic, "%d %lu %s: vif still assoc; not deleting keys\n", curthread->td_tid, jiffies, __func__); + ieee80211_free_node(ni); return (0); } if (IEEE80211_KEY_UNDEFINED(k)) { ic_printf(ic, "%s: vap %p key %p is undefined: %p %u\n", __func__, vap, k, k->wk_cipher, k->wk_keyix); + ieee80211_free_node(ni); return (0); } if (vap->iv_bss == NULL) { ic_printf(ic, "%s: iv_bss %p for vap %p is NULL\n", __func__, vap->iv_bss, vap); - return (0); - } - - ni = ieee80211_ref_node(vap->iv_bss); - lsta = ni->ni_drv_data; - if (lsta == NULL) { - ic_printf(ic, "%s: ni %p (%6D) with lsta NULL\n", - __func__, ni, ni->ni_bssid, ":"); ieee80211_free_node(ni); return (0); } @@ -1943,7 +2004,7 @@ lkpi_update_dtim_tsf(struct ieee80211_vif *vif, struct ieee80211_node *ni, * we set the BSS_CHANGED_BEACON_INFO on the non-teardown * path so make sure we only do run this check once we are * assoc. (*iv_recv_mgmt)() will be called before we enter - * here so the ni will be updates with information from the + * here so the ni will be updated with information from the * beacon via net80211::sta_recv_mgmt(). We also need to * make sure we do not do it on every beacon we still may * get so only do if something changed. vif->bss_conf.dtim_period @@ -2060,7 +2121,7 @@ lkpi_disassoc(struct ieee80211_sta *sta, struct ieee80211_vif *vif, * The caller is responsible for removing the sta gong to * IEEE80211_STA_NOTEXIST and then executing the * bss_info_changed() update. - * See lkpi_sta_run_to_init() for more detailed comment. + * See DOWN4 for more detailed comment. */ lvif = VIF_TO_LVIF(vif); @@ -2101,7 +2162,7 @@ lkpi_wake_tx_queues(struct ieee80211_hw *hw, struct ieee80211_sta *sta, if (no_emptyq && ltxq_empty) continue; - lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]); + lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid], false); } } @@ -2181,19 +2242,70 @@ lkpi_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif) free(lchanctx, M_LKPI80211); } +/* -------------------------------------------------------------------------- */ + +/* Any other options belong here? Check more drivers. */ +#define BSS_CHANGED_VIF_CFG_BITS \ + (BSS_CHANGED_SSID | BSS_CHANGED_IDLE | BSS_CHANGED_PS | BSS_CHANGED_ASSOC | \ + BSS_CHANGED_ARP_FILTER | BSS_CHANGED_MLD_VALID_LINKS | BSS_CHANGED_MLD_TTLM) + +static void +lkpi_bss_info_change(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_bss_changed bss_changed) +{ + struct lkpi_vif *lvif; + enum ieee80211_bss_changed vif_cfg_bits, link_info_bits; + + if (ieee80211_vif_is_mld(vif)) { + TODO("This likely needs a subset only; split up into 3 parts."); + } + + /* Nothing to do? */ + if (bss_changed == 0) + return; + + /* + * If the vif is not known to the driver there is nothing to notifiy for. + * We MUST NOT check for !lvif_bss_synched here (the reasonable it seems) + * as we need to execute the update(s) or we will have follow-up issues. + */ + lvif = VIF_TO_LVIF(vif); + if (!lvif->added_to_drv) + return; + + /* + * With the advent of MLO bss_conf got split up into vif and link + * change notfications, while historically it was one. + * We now need to support all possible models. + */ + vif_cfg_bits = bss_changed & BSS_CHANGED_VIF_CFG_BITS; + if (vif_cfg_bits != 0) + lkpi_80211_mo_vif_cfg_changed(hw, vif, vif_cfg_bits, false); + + link_info_bits = bss_changed & ~(BSS_CHANGED_VIF_CFG_BITS); + if (link_info_bits != 0) + lkpi_80211_mo_link_info_changed(hw, vif, &vif->bss_conf, + link_info_bits, 0, false); + + lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed); +} /* -------------------------------------------------------------------------- */ static int lkpi_sta_state_do_nada(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { - return (0); } -/* lkpi_iv_newstate() handles the stop scan case generally. */ -#define lkpi_sta_scan_to_init(_v, _n, _a) lkpi_sta_state_do_nada(_v, _n, _a) +/* UP1 */ +static int +lkpi_sta_init_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + return (lkpi_sta_state_do_nada(vap, nstate, arg)); +} +/* UP2 */ static int lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { @@ -2392,7 +2504,7 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int /* RATES */ IMPROVE("bss info: not all needs to come now and rates are missing"); - lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed); + lkpi_bss_info_change(hw, vif, bss_changed); /* * Given ni and lsta are 1:1 from alloc to free we can assert that @@ -2437,7 +2549,9 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int /* Start mgd_prepare_tx. */ memset(&prep_tx_info, 0, sizeof(prep_tx_info)); - prep_tx_info.duration = PREP_TX_INFO_DURATION; + prep_tx_info.duration = PREP_TX_INFO_DURATION; /* SAE */ + prep_tx_info.subtype = IEEE80211_STYPE_AUTH; + prep_tx_info.link_id = 0; lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info); lsta->in_mgd = true; @@ -2518,136 +2632,7 @@ out_relocked: return (error); } -static int -lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) -{ - struct lkpi_hw *lhw; - struct ieee80211_hw *hw; - struct lkpi_vif *lvif; - struct ieee80211_vif *vif; - struct ieee80211_node *ni; - struct lkpi_sta *lsta; - struct ieee80211_sta *sta; - struct ieee80211_prep_tx_info prep_tx_info; - enum ieee80211_bss_changed bss_changed; - int error; - - lhw = vap->iv_ic->ic_softc; - hw = LHW_TO_HW(lhw); - lvif = VAP_TO_LVIF(vap); - vif = LVIF_TO_VIF(lvif); - - LKPI_80211_LVIF_LOCK(lvif); -#ifdef LINUXKPI_DEBUG_80211 - /* XXX-BZ KASSERT later; state going down so no action. */ - if (lvif->lvif_bss == NULL) - ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p " - "lvif_bss->ni %p synched %d\n", __func__, __LINE__, - lvif, vap, vap->iv_bss, lvif->lvif_bss, - (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL, - lvif->lvif_bss_synched); -#endif - - lsta = lvif->lvif_bss; - LKPI_80211_LVIF_UNLOCK(lvif); - KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p " - "lvif %p vap %p\n", __func__, - lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap)); - ni = lsta->ni; /* Reference held for lvif_bss. */ - sta = LSTA_TO_STA(lsta); - - lkpi_lsta_dump(lsta, ni, __func__, __LINE__); - - IEEE80211_UNLOCK(vap->iv_ic); - wiphy_lock(hw->wiphy); - - /* flush, drop. */ - lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true); - - /* Wake tx queues to get packet(s) out. */ - lkpi_wake_tx_queues(hw, sta, false, true); - - /* flush, no drop */ - lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false); - - /* End mgd_complete_tx. */ - if (lsta->in_mgd) { - memset(&prep_tx_info, 0, sizeof(prep_tx_info)); - prep_tx_info.success = false; - lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); - lsta->in_mgd = false; - } - - /* sync_rx_queues */ - lkpi_80211_mo_sync_rx_queues(hw); - - /* sta_pre_rcu_remove */ - lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta); - - /* Take the station down. */ - - /* Adjust sta and change state (from NONE) to NOTEXIST. */ - KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); - KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not " - "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg)); - error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NOTEXIST); - if (error != 0) { - IMPROVE("do we need to undo the chan ctx?"); - ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NOTEXIST) " - "failed: %d\n", __func__, __LINE__, error); - goto out; - } -#if 0 - lsta->added_to_drv = false; /* mo manages. */ -#endif - - bss_changed = 0; - vif->bss_conf.dtim_period = 0; /* go back to 0. */ - bss_changed |= BSS_CHANGED_BEACON_INFO; - lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed); - - lkpi_lsta_dump(lsta, ni, __func__, __LINE__); - - LKPI_80211_LVIF_LOCK(lvif); - /* Remove ni reference for this cache of lsta. */ - lvif->lvif_bss = NULL; - lvif->lvif_bss_synched = false; - LKPI_80211_LVIF_UNLOCK(lvif); - lkpi_lsta_remove(lsta, lvif); - - /* conf_tx */ - - lkpi_remove_chanctx(hw, vif); - -out: - wiphy_unlock(hw->wiphy); - IEEE80211_LOCK(vap->iv_ic); - if (error == 0) { - /* - * We do this outside the wiphy lock as net80211::node_free() may call - * into crypto code to delete keys and we have a recursed on - * non-recursive sx panic. Also only do this if we get here w/o error. - * - * The very last release the reference on the ni for the ni/lsta on - * lvif->lvif_bss. Upon return from this both ni and lsta are invalid - * and potentially freed. - */ - ieee80211_free_node(ni); - } - return (error); -} - -static int -lkpi_sta_auth_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) -{ - int error; - - error = lkpi_sta_auth_to_scan(vap, nstate, arg); - if (error == 0) - error = lkpi_sta_scan_to_init(vap, nstate, arg); - return (error); -} - +/* UP3.1 */ static int lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { @@ -2702,23 +2687,28 @@ lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, in /* End mgd_complete_tx. */ if (lsta->in_mgd) { memset(&prep_tx_info, 0, sizeof(prep_tx_info)); + prep_tx_info.subtype = IEEE80211_STYPE_AUTH; prep_tx_info.success = true; lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); lsta->in_mgd = false; } - /* Now start assoc. */ + /* Now start assoc. unless nstate=RUN (auth_to_run). */ /* Start mgd_prepare_tx. */ - if (!lsta->in_mgd) { + if (nstate == IEEE80211_S_ASSOC && !lsta->in_mgd) { memset(&prep_tx_info, 0, sizeof(prep_tx_info)); - prep_tx_info.duration = PREP_TX_INFO_DURATION; + prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ; + prep_tx_info.link_id = 0; lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info); lsta->in_mgd = true; } +#if 0 + /* We do not yet have a packet to go out. */ /* Wake tx queue to get packet out. */ lkpi_wake_tx_queues(hw, LSTA_TO_STA(lsta), false, true); +#endif /* * <twiddle> .. we end up in "assoc_to_run" @@ -2736,286 +2726,21 @@ out: return (error); } -/* auth_to_auth, assoc_to_assoc. */ -static int -lkpi_sta_a_to_a(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) -{ - struct lkpi_hw *lhw; - struct ieee80211_hw *hw; - struct lkpi_vif *lvif; - struct ieee80211_vif *vif; - struct lkpi_sta *lsta; - struct ieee80211_prep_tx_info prep_tx_info; - int error; - - lhw = vap->iv_ic->ic_softc; - hw = LHW_TO_HW(lhw); - lvif = VAP_TO_LVIF(vap); - vif = LVIF_TO_VIF(lvif); - - IEEE80211_UNLOCK(vap->iv_ic); - wiphy_lock(hw->wiphy); - - LKPI_80211_LVIF_LOCK(lvif); - /* XXX-BZ KASSERT later? */ - if (!lvif->lvif_bss_synched || lvif->lvif_bss == NULL) { -#ifdef LINUXKPI_DEBUG_80211 - ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p " - "lvif_bss->ni %p synched %d\n", __func__, __LINE__, - lvif, vap, vap->iv_bss, lvif->lvif_bss, - (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL, - lvif->lvif_bss_synched); -#endif - LKPI_80211_LVIF_UNLOCK(lvif); - error = ENOTRECOVERABLE; - goto out; - } - lsta = lvif->lvif_bss; - LKPI_80211_LVIF_UNLOCK(lvif); - - KASSERT(lsta != NULL, ("%s: lsta %p! lvif %p vap %p\n", __func__, - lsta, lvif, vap)); - - IMPROVE("event callback?"); - - /* End mgd_complete_tx. */ - if (lsta->in_mgd) { - memset(&prep_tx_info, 0, sizeof(prep_tx_info)); - prep_tx_info.success = false; - lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); - lsta->in_mgd = false; - } - - /* Now start assoc. */ - - /* Start mgd_prepare_tx. */ - if (!lsta->in_mgd) { - memset(&prep_tx_info, 0, sizeof(prep_tx_info)); - prep_tx_info.duration = PREP_TX_INFO_DURATION; - lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info); - lsta->in_mgd = true; - } - - error = 0; -out: - wiphy_unlock(hw->wiphy); - IEEE80211_LOCK(vap->iv_ic); - - return (error); -} - -static int -_lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) -{ - struct lkpi_hw *lhw; - struct ieee80211_hw *hw; - struct lkpi_vif *lvif; - struct ieee80211_vif *vif; - struct ieee80211_node *ni; - struct lkpi_sta *lsta; - struct ieee80211_sta *sta; - struct ieee80211_prep_tx_info prep_tx_info; - enum ieee80211_bss_changed bss_changed; - int error; - - lhw = vap->iv_ic->ic_softc; - hw = LHW_TO_HW(lhw); - lvif = VAP_TO_LVIF(vap); - vif = LVIF_TO_VIF(lvif); - - IEEE80211_UNLOCK(vap->iv_ic); - wiphy_lock(hw->wiphy); - - LKPI_80211_LVIF_LOCK(lvif); -#ifdef LINUXKPI_DEBUG_80211 - /* XXX-BZ KASSERT later; state going down so no action. */ - if (lvif->lvif_bss == NULL) - ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p " - "lvif_bss->ni %p synched %d\n", __func__, __LINE__, - lvif, vap, vap->iv_bss, lvif->lvif_bss, - (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL, - lvif->lvif_bss_synched); -#endif - lsta = lvif->lvif_bss; - LKPI_80211_LVIF_UNLOCK(lvif); - KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p " - "lvif %p vap %p\n", __func__, - lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap)); - - ni = lsta->ni; /* Reference held for lvif_bss. */ - sta = LSTA_TO_STA(lsta); - - lkpi_lsta_dump(lsta, ni, __func__, __LINE__); - - /* flush, drop. */ - lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true); - - IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?"); - if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) && - !lsta->in_mgd) { - memset(&prep_tx_info, 0, sizeof(prep_tx_info)); - prep_tx_info.duration = PREP_TX_INFO_DURATION; - prep_tx_info.was_assoc = true; - lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info); - lsta->in_mgd = true; - } - - wiphy_unlock(hw->wiphy); - IEEE80211_LOCK(vap->iv_ic); - - /* Call iv_newstate first so we get potential DEAUTH packet out. */ - error = lvif->iv_newstate(vap, nstate, arg); - if (error != 0) { - ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) " - "failed: %d\n", __func__, __LINE__, vap, nstate, arg, error); - goto outni; - } - - IEEE80211_UNLOCK(vap->iv_ic); - - /* Ensure the packets get out. */ - lkpi_80211_flush_tx(lhw, lsta); - - wiphy_lock(hw->wiphy); - - lkpi_lsta_dump(lsta, ni, __func__, __LINE__); - - /* Wake tx queues to get packet(s) out. */ - lkpi_wake_tx_queues(hw, sta, false, true); - - /* flush, no drop */ - lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false); - - /* End mgd_complete_tx. */ - if (lsta->in_mgd) { - memset(&prep_tx_info, 0, sizeof(prep_tx_info)); - prep_tx_info.success = false; - prep_tx_info.was_assoc = true; - lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); - lsta->in_mgd = false; - } - - /* sync_rx_queues */ - lkpi_80211_mo_sync_rx_queues(hw); - - /* sta_pre_rcu_remove */ - lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta); - - /* Take the station down. */ - - /* Update sta and change state (from AUTH) to NONE. */ - KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); - KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not " - "AUTH: %#x\n", __func__, lsta, lsta->state)); - error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE); - if (error != 0) { - ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) " - "failed: %d\n", __func__, __LINE__, error); - goto out; - } - - /* See comment in lkpi_sta_run_to_init(). */ - bss_changed = 0; - bss_changed |= lkpi_disassoc(sta, vif, lhw); - -#ifdef LKPI_80211_HW_CRYPTO - /* - * In theory we remove keys here but there must not exist any for this - * state change until we clean them up again into small steps and no - * code duplication. - */ -#endif - - lkpi_lsta_dump(lsta, ni, __func__, __LINE__); - - /* Adjust sta and change state (from NONE) to NOTEXIST. */ - KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); - KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not " - "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg)); - error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NOTEXIST); - if (error != 0) { - IMPROVE("do we need to undo the chan ctx?"); - ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NOTEXIST) " - "failed: %d\n", __func__, __LINE__, error); - goto out; - } - - lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* sta no longer save to use. */ - - IMPROVE("Any bss_info changes to announce?"); - vif->bss_conf.qos = 0; - bss_changed |= BSS_CHANGED_QOS; - vif->cfg.ssid_len = 0; - memset(vif->cfg.ssid, '\0', sizeof(vif->cfg.ssid)); - bss_changed |= BSS_CHANGED_BSSID; - vif->bss_conf.dtim_period = 0; /* go back to 0. */ - bss_changed |= BSS_CHANGED_BEACON_INFO; - lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed); - - LKPI_80211_LVIF_LOCK(lvif); - /* Remove ni reference for this cache of lsta. */ - lvif->lvif_bss = NULL; - lvif->lvif_bss_synched = false; - LKPI_80211_LVIF_UNLOCK(lvif); - lkpi_lsta_remove(lsta, lvif); - - /* conf_tx */ - - lkpi_remove_chanctx(hw, vif); - - error = EALREADY; -out: - wiphy_unlock(hw->wiphy); - IEEE80211_LOCK(vap->iv_ic); - if (error == EALREADY) { - /* - * We do this outside the wiphy lock as net80211::node_free() may call - * into crypto code to delete keys and we have a recursed on - * non-recursive sx panic. Also only do this if we get here w/o error. - * - * The very last release the reference on the ni for the ni/lsta on - * lvif->lvif_bss. Upon return from this both ni and lsta are invalid - * and potentially freed. - */ - ieee80211_free_node(ni); - } -outni: - return (error); -} - -static int -lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) -{ - int error; - - error = _lkpi_sta_assoc_to_down(vap, nstate, arg); - if (error != 0 && error != EALREADY) - return (error); - - /* At this point iv_bss is long a new node! */ - - error |= lkpi_sta_scan_to_auth(vap, nstate, 0); - return (error); -} - -static int -lkpi_sta_assoc_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) -{ - int error; - - error = _lkpi_sta_assoc_to_down(vap, nstate, arg); - return (error); -} +static int lkpi_sta_assoc_to_run(struct ieee80211vap *, enum ieee80211_state, int); +/* UP3.2 */ static int -lkpi_sta_assoc_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +lkpi_sta_auth_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { int error; - error = _lkpi_sta_assoc_to_down(vap, nstate, arg); + error = lkpi_sta_auth_to_assoc(vap, nstate, arg); + if (error == 0) + error = lkpi_sta_assoc_to_run(vap, nstate, arg); return (error); } +/* UP4 */ static int lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { @@ -3063,7 +2788,7 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int IMPROVE("ponder some of this moved to ic_newassoc, scan_assoc_success, " "and to lesser extend ieee80211_notify_node_join"); - /* Finish assoc. */ + /* Finish assoc. (even if this is auth_to_run!) */ /* Update sta_state (AUTH to ASSOC) and set aid. */ KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not " "AUTH: %#x\n", __func__, lsta, lsta->state)); @@ -3113,16 +2838,18 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int } bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__); - lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed); + lkpi_bss_info_change(hw, vif, bss_changed); /* - change_chanctx (if needed) * - event_callback */ - /* End mgd_complete_tx. */ + /* End mgd_complete_tx. (we do not have to check ostate == IEEE80211_S_ASSOC). */ if (lsta->in_mgd) { memset(&prep_tx_info, 0, sizeof(prep_tx_info)); - prep_tx_info.success = true; + prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ; + prep_tx_info.success = true; /* Needs vif->cfg.assoc set! */ + prep_tx_info.link_id = 0; lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); lsta->in_mgd = false; } @@ -3171,7 +2898,7 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int bss_changed = 0; bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__); - lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed); + lkpi_bss_info_change(hw, vif, bss_changed); /* Prepare_multicast && configure_filter. */ lkpi_update_mcast_filter(vap->iv_ic); @@ -3182,17 +2909,14 @@ out: return (error); } -static int -lkpi_sta_auth_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) -{ - int error; - - error = lkpi_sta_auth_to_assoc(vap, nstate, arg); - if (error == 0) - error = lkpi_sta_assoc_to_run(vap, nstate, arg); - return (error); -} - +/* + * DOWN1 + * "to assoc" means we are going back to State 2 from State 4[/3]. + * This means ni still is authenticated, so we keep sta, chanctx, .. + * We will send a (Re)Assoc Request in case net80211 handles roadming. + * Note: this can be called as part of a DEAUTH going to State 1 as well, + * so for RoC prep_tx_info we need to check nstate (see run_to_{auth,scan,init}). + */ static int lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { @@ -3207,6 +2931,7 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int #if 0 enum ieee80211_bss_changed bss_changed; #endif + struct ieee80211_rx_ampdu *rap; int error; lhw = vap->iv_ic->ic_softc; @@ -3214,6 +2939,9 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int lvif = VAP_TO_LVIF(vap); vif = LVIF_TO_VIF(lvif); + IEEE80211_UNLOCK(vap->iv_ic); + wiphy_lock(hw->wiphy); + LKPI_80211_LVIF_LOCK(lvif); #ifdef LINUXKPI_DEBUG_80211 /* XXX-BZ KASSERT later; state going down so no action. */ @@ -3235,26 +2963,41 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int lkpi_lsta_dump(lsta, ni, __func__, __LINE__); - IEEE80211_UNLOCK(vap->iv_ic); - wiphy_lock(hw->wiphy); - /* flush, drop. */ lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true); - IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?"); - if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) && - !lsta->in_mgd) { - memset(&prep_tx_info, 0, sizeof(prep_tx_info)); - prep_tx_info.duration = PREP_TX_INFO_DURATION; - prep_tx_info.was_assoc = true; - lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info); - lsta->in_mgd = true; + /* We should make this a KASSERT. */ + if (lsta->in_mgd) { + ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p lsta %p in_mgd\n", + __func__, __LINE__, lvif, vap, lsta); } + /* + * Problem is that we should hook into the tx/rx flow and not + * try to re-model the state machine parts. We may miss a SME + * triggered frame this way. + */ + memset(&prep_tx_info, 0, sizeof(prep_tx_info)); + if (nstate == IEEE80211_S_ASSOC) { + if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) { + if (arg) + prep_tx_info.subtype = IEEE80211_STYPE_REASSOC_REQ; + else + prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ; + } else { + /* wpa_supplicant upon RTM_IEEE80211_LEAVE. */ + prep_tx_info.subtype = IEEE80211_STYPE_DISASSOC; + } + } else + prep_tx_info.subtype = IEEE80211_STYPE_DEAUTH; + prep_tx_info.was_assoc = true; + prep_tx_info.link_id = 0; + lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = true; wiphy_unlock(hw->wiphy); IEEE80211_LOCK(vap->iv_ic); - /* Call iv_newstate first so we get potential DISASSOC packet out. */ + /* Call iv_newstate first so we get potential (RE-)ASSOC/DEAUTH? packet out. */ error = lvif->iv_newstate(vap, nstate, arg); if (error != 0) { ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) " @@ -3262,6 +3005,16 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int goto outni; } + /* Stop any BA sessions if still active. */ + for (int rapn = 0; rapn < WME_NUM_TID; rapn++) { + rap = &ni->ni_rx_ampdu[rapn]; + + if ((rap->rxa_flags & IEEE80211_AGGR_RUNNING) == 0) + continue; + + vap->iv_ic->ic_ampdu_rx_stop(ni, rap); + } + IEEE80211_UNLOCK(vap->iv_ic); /* Ensure the packets get out. */ @@ -3278,20 +3031,20 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false); /* End mgd_complete_tx. */ - if (lsta->in_mgd) { - memset(&prep_tx_info, 0, sizeof(prep_tx_info)); - prep_tx_info.success = false; - prep_tx_info.was_assoc = true; - lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); - lsta->in_mgd = false; + /* We should make this a KASSERT. */ + if (!lsta->in_mgd) { + ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p lsta %p !in_mgd\n", + __func__, __LINE__, lvif, vap, lsta); } + lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = false; #if 0 /* sync_rx_queues */ lkpi_80211_mo_sync_rx_queues(hw); /* sta_pre_rcu_remove */ - lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta); + lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta); #endif /* Take the station down. */ @@ -3340,6 +3093,7 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int #if 0 /* Update bss info (bss_info_changed) (assoc, aid, ..). */ + /* See comment in DOWN4. */ lkpi_disassoc(sta, vif, lhw); #endif @@ -3351,8 +3105,15 @@ outni: return (error); } +/* + * DOWN2 + * We are in state 2 and go back to state 1 and will try to auth again + * (to IEEE80211_S_AUTH in FreeBSD means "try to auth"). This should be + * like scan_to_auth but that we keep the "ni" and with that chanctx/bssid, + * which essentially makes this "a_to_a" in LinuxKPI. + */ static int -lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct lkpi_hw *lhw; struct ieee80211_hw *hw; @@ -3360,9 +3121,7 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int struct ieee80211_vif *vif; struct ieee80211_node *ni; struct lkpi_sta *lsta; - struct ieee80211_sta *sta; struct ieee80211_prep_tx_info prep_tx_info; - enum ieee80211_bss_changed bss_changed; int error; lhw = vap->iv_ic->ic_softc; @@ -3390,43 +3149,107 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap)); ni = lsta->ni; /* Reference held for lvif_bss. */ - sta = LSTA_TO_STA(lsta); lkpi_lsta_dump(lsta, ni, __func__, __LINE__); - /* flush, drop. */ - lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true); - - IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?"); - if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) && - !lsta->in_mgd) { + /* End mgd_complete_tx. */ + if (lsta->in_mgd && vap->iv_state == IEEE80211_S_ASSOC) { memset(&prep_tx_info, 0, sizeof(prep_tx_info)); - prep_tx_info.duration = PREP_TX_INFO_DURATION; - prep_tx_info.was_assoc = true; - lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info); - lsta->in_mgd = true; + prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ; + prep_tx_info.link_id = 0; + lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = false; + } else if (lsta->in_mgd) { + ic_printf(vap->iv_ic, "%s:%d: in_mgd %d (%s) -> %d (%s) %d\n", + __func__, __LINE__, + vap->iv_state, ieee80211_state_name[vap->iv_state], + nstate, ieee80211_state_name[nstate], arg); } - wiphy_unlock(hw->wiphy); - IEEE80211_LOCK(vap->iv_ic); - - /* Call iv_newstate first so we get potential DISASSOC packet out. */ - error = lvif->iv_newstate(vap, nstate, arg); + /* Take the station down. */ + /* Update sta_state (AUTH to NONE). */ + KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); + KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not " + "AUTH: %#x\n", __func__, lsta, lsta->state)); + error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE); if (error != 0) { - ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) " - "failed: %d\n", __func__, __LINE__, vap, nstate, arg, error); - goto outni; + ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) " + "failed: %d\n", __func__, __LINE__, error); + goto out; } - IEEE80211_UNLOCK(vap->iv_ic); + lkpi_lsta_dump(lsta, ni, __func__, __LINE__); - /* Ensure the packets get out. */ - lkpi_80211_flush_tx(lhw, lsta); +out: + wiphy_unlock(hw->wiphy); + IEEE80211_LOCK(vap->iv_ic); + return (error); +} + +/* + * DOWN3 + * We are in state 1. Either auth timed out (arg != 0) or we have an internal + * state change forcing us to give up trying to authenticate. + * Cleanup and remove chanctx, sta, ... + */ +static int +lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + struct ieee80211_node *ni; + struct lkpi_sta *lsta; + struct ieee80211_sta *sta; + struct ieee80211_prep_tx_info prep_tx_info; + enum ieee80211_bss_changed bss_changed; + int error; + + lhw = vap->iv_ic->ic_softc; + hw = LHW_TO_HW(lhw); + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + IEEE80211_UNLOCK(vap->iv_ic); wiphy_lock(hw->wiphy); + LKPI_80211_LVIF_LOCK(lvif); + /* + * XXX-BZ KASSERT later; state going down so no action in theory + * but try to avoid a NULL-pointer derref for now and gracefully + * fail for non-debug kernels. + */ + if (lvif->lvif_bss == NULL) { + ic_printf(vap->iv_ic, "%s:%d: ERROR: lvif %p vap %p iv_bss %p " + "lvif_bss %p lvif_bss->ni %p synched %d; " + "expect follow-up problems\n", __func__, __LINE__, + lvif, vap, vap->iv_bss, lvif->lvif_bss, + (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL, + lvif->lvif_bss_synched); + LKPI_80211_LVIF_UNLOCK(lvif); + /* + * This will likely lead to a firmware crash (if there + * was not one before already) and need a + * ieee80211_restart_hw() but still better than a panic + * for users as they can at least recover. + */ + error = ENOTRECOVERABLE; + goto out; + } + lsta = lvif->lvif_bss; + LKPI_80211_LVIF_UNLOCK(lvif); + KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p " + "lvif %p vap %p\n", __func__, + lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap)); + ni = lsta->ni; /* Reference held for lvif_bss. */ + sta = LSTA_TO_STA(lsta); + lkpi_lsta_dump(lsta, ni, __func__, __LINE__); + /* flush, drop. */ + lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true); + /* Wake tx queues to get packet(s) out. */ lkpi_wake_tx_queues(hw, sta, false, true); @@ -3436,8 +3259,8 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int /* End mgd_complete_tx. */ if (lsta->in_mgd) { memset(&prep_tx_info, 0, sizeof(prep_tx_info)); - prep_tx_info.success = false; - prep_tx_info.was_assoc = true; + prep_tx_info.subtype = IEEE80211_STYPE_AUTH; + prep_tx_info.link_id = 0; lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); lsta->in_mgd = false; } @@ -3445,36 +3268,8 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int /* sync_rx_queues */ lkpi_80211_mo_sync_rx_queues(hw); - /* sta_pre_rcu_remove */ - lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta); - - /* Take the station down. */ - - /* Adjust sta and change state (from AUTHORIZED) to ASSOC. */ - KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); - KASSERT(lsta->state == IEEE80211_STA_AUTHORIZED, ("%s: lsta %p state not " - "AUTHORIZED: %#x\n", __func__, lsta, lsta->state)); - error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_ASSOC); - if (error != 0) { - ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(ASSOC) " - "failed: %d\n", __func__, __LINE__, error); - goto out; - } - - lkpi_lsta_dump(lsta, ni, __func__, __LINE__); - #ifdef LKPI_80211_HW_CRYPTO if (lkpi_hwcrypto) { - /* - * In theory we only need to do this if we changed assoc. - * If we were not assoc, there should be no keys and we - * should not be here. - */ -#ifdef notyet - KASSERT((bss_changed & BSS_CHANGED_ASSOC) != 0, ("%s: " - "trying to remove keys but were not assoc: %#010jx, lvif %p\n", - __func__, (uintmax_t)bss_changed, lvif)); -#endif error = lkpi_sta_del_keys(hw, vif, lsta); if (error != 0) { ic_printf(vap->iv_ic, "%s:%d: lkpi_sta_del_keys " @@ -3489,50 +3284,36 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int } #endif - /* Update sta_state (ASSOC to AUTH). */ - KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); - KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not " - "ASSOC: %#x\n", __func__, lsta, lsta->state)); - error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_AUTH); - if (error != 0) { - ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(AUTH) " - "failed: %d\n", __func__, __LINE__, error); - goto out; - } + /* sta_pre_rcu_remove */ + lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta); - lkpi_lsta_dump(lsta, ni, __func__, __LINE__); + synchronize_net(); - /* Update sta and change state (from AUTH) to NONE. */ - KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); - KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not " - "AUTH: %#x\n", __func__, lsta, lsta->state)); - error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE); - if (error != 0) { - ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) " - "failed: %d\n", __func__, __LINE__, error); - goto out; - } + /* Take the station down. */ bss_changed = 0; /* - * Start updating bss info (bss_info_changed) (assoc, aid, ..). + * Start updating bss info (*bss_info_changed) (assoc, aid, ..). * - * One would expect this to happen when going off AUTHORIZED. - * See comment there; removes the sta from fw if not careful - * (bss_info_changed() change is executed right away). + * One would expect this to happen when going off AUTHORIZED but + * not so. * - * We need to do this now, before sta changes to IEEE80211_STA_NOTEXIST - * as otherwise drivers (iwlwifi at least) will silently not remove - * the sta from the firmware and when we will add a new one trigger - * a fw assert. + * Immediately issuing the (*bss_info_changed) used to also remove the + * sta from firmware for iwlwifi; or we have problems with the sta + * silently not being removed and then crash upon the next sta add. + * Neither seems to be the case or a problem still. * - * The order which works best so far avoiding early removal or silent - * non-removal seems to be (for iwlwifi::mld-mac80211.c cases; - * the iwlwifi:mac80211.c case still to be tested): - * 1) lkpi_disassoc(): set vif->cfg.assoc = false (aid=0 side effect here) - * 2) call the last sta_state update -> IEEE80211_STA_NOTEXIST - * (removes the sta given assoc is false) - * 3) add the remaining BSS_CHANGED changes and call bss_info_changed() + * Contrary for BE200 (iwlwifi/mld) if we do not issue the + * (*vif_cfg_change) to tell FW that we are no longer assoc + * it will crash now upon sta rm. So the order now is as we once + * expected it: + * + * 1) lkpi_disassoc(): set vif->cfg.assoc = false and .aid=0 + * 2) add the remaining BSS_CHANGED changes and call (*bss_info_changed) + * (which may be split up into (*vif_cfg_change) and + * (*link_info_changed) for more modern drivers). + * 3) call the last sta_state update -> IEEE80211_STA_NOTEXIST + * (removes the sta given assoc is false) and tidy up our lists. * 4) call unassign_vif_chanctx * 5) call lkpi_hw_conf_idle * 6) call remove_chanctx @@ -3544,6 +3325,18 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int lkpi_lsta_dump(lsta, ni, __func__, __LINE__); + IMPROVE("Any bss_info changes to announce?"); + vif->bss_conf.qos = false; + bss_changed |= BSS_CHANGED_QOS; + vif->cfg.ssid_len = 0; + memset(vif->cfg.ssid, '\0', sizeof(vif->cfg.ssid)); + bss_changed |= BSS_CHANGED_BSSID; + vif->bss_conf.use_short_preamble = false; + /* XXX BSS_CHANGED_???? */ + vif->bss_conf.dtim_period = 0; /* go back to 0. */ + bss_changed |= BSS_CHANGED_BEACON_INFO; + lkpi_bss_info_change(hw, vif, bss_changed); + /* Adjust sta and change state (from NONE) to NOTEXIST. */ KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not " @@ -3558,20 +3351,7 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int lkpi_lsta_remove(lsta, lvif); - lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* sta no longer save to use. */ - - IMPROVE("Any bss_info changes to announce?"); - vif->bss_conf.qos = 0; - bss_changed |= BSS_CHANGED_QOS; - vif->cfg.ssid_len = 0; - memset(vif->cfg.ssid, '\0', sizeof(vif->cfg.ssid)); - bss_changed |= BSS_CHANGED_BSSID; - vif->bss_conf.use_short_preamble = false; - vif->bss_conf.qos = false; - /* XXX BSS_CHANGED_???? */ - vif->bss_conf.dtim_period = 0; /* go back to 0. */ - bss_changed |= BSS_CHANGED_BEACON_INFO; - lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed); + lkpi_lsta_dump(lsta, ni, __func__, __LINE__); LKPI_80211_LVIF_LOCK(lvif); /* Remove ni reference for this cache of lsta. */ @@ -3583,11 +3363,10 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int lkpi_remove_chanctx(hw, vif); - error = EALREADY; out: wiphy_unlock(hw->wiphy); IEEE80211_LOCK(vap->iv_ic); - if (error == EALREADY) { + if (error == 0) { /* * We do this outside the wiphy lock as net80211::node_free() may call * into crypto code to delete keys and we have a recursed on @@ -3599,15 +3378,156 @@ out: */ ieee80211_free_node(ni); } -outni: + return (error); +} + +/* DOWN4 */ +static int +lkpi_sta_scan_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + /* lkpi_iv_newstate() handles the stop scan case in common code. */ + return (lkpi_sta_state_do_nada(vap, nstate, arg)); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static int +lkpi_sta_auth_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + int error; + + error = lkpi_sta_auth_to_scan(vap, nstate, arg); + if (error == 0) + error = lkpi_sta_scan_to_init(vap, nstate, arg); + return (error); +} + +/* auth_to_auth, assoc_to_assoc. */ +static int +lkpi_sta_a_to_a(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + struct lkpi_sta *lsta; + struct ieee80211_prep_tx_info prep_tx_info; + int error; + + lhw = vap->iv_ic->ic_softc; + hw = LHW_TO_HW(lhw); + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + + IEEE80211_UNLOCK(vap->iv_ic); + wiphy_lock(hw->wiphy); + + LKPI_80211_LVIF_LOCK(lvif); + /* XXX-BZ KASSERT later? */ + if (!lvif->lvif_bss_synched || lvif->lvif_bss == NULL) { +#ifdef LINUXKPI_DEBUG_80211 + ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p " + "lvif_bss->ni %p synched %d\n", __func__, __LINE__, + lvif, vap, vap->iv_bss, lvif->lvif_bss, + (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL, + lvif->lvif_bss_synched); +#endif + LKPI_80211_LVIF_UNLOCK(lvif); + error = ENOTRECOVERABLE; + goto out; + } + lsta = lvif->lvif_bss; + LKPI_80211_LVIF_UNLOCK(lvif); + + KASSERT(lsta != NULL, ("%s: lsta %p! lvif %p vap %p\n", __func__, + lsta, lvif, vap)); + + IMPROVE("event callback?"); + + /* End mgd_complete_tx. */ + if (lsta->in_mgd) { + memset(&prep_tx_info, 0, sizeof(prep_tx_info)); + if (vap->iv_state == IEEE80211_S_AUTH) + prep_tx_info.subtype = IEEE80211_STYPE_AUTH; + else + prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ; + prep_tx_info.link_id = 0; + lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = false; + } + + /* Now start auth/assoc. */ + + /* Start mgd_prepare_tx. */ + if (!lsta->in_mgd) { + memset(&prep_tx_info, 0, sizeof(prep_tx_info)); + if (nstate == IEEE80211_S_AUTH) + prep_tx_info.subtype = IEEE80211_STYPE_AUTH; + else + prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ; + prep_tx_info.link_id = 0; + lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = true; + } + + error = 0; +out: + wiphy_unlock(hw->wiphy); + IEEE80211_LOCK(vap->iv_ic); + + return (error); +} + +static int +lkpi_sta_assoc_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + int error; + + error = lkpi_sta_assoc_to_auth(vap, nstate, arg); + if (error != 0 && error != EALREADY) + return (error); + + error = lkpi_sta_auth_to_scan(vap, nstate, arg); + return (error); +} + +static int +lkpi_sta_assoc_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + int error; + + error = lkpi_sta_assoc_to_scan(vap, nstate, arg); + if (error != 0 && error != EALREADY) + return (error); + + error = lkpi_sta_scan_to_init(vap, nstate, arg); /* do_nada */ + return (error); +} + +static int +lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + int error; + + error = lkpi_sta_run_to_assoc(vap, nstate, arg); + if (error != 0 && error != EALREADY) + return (error); + + error = lkpi_sta_assoc_to_init(vap, nstate, arg); return (error); } static int lkpi_sta_run_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { + int error; + + error = lkpi_sta_run_to_assoc(vap, nstate, arg); + if (error != 0 && error != EALREADY) + return (error); - return (lkpi_sta_run_to_init(vap, nstate, arg)); + error = lkpi_sta_assoc_to_scan(vap, nstate, arg); + return (error); } static int @@ -3615,13 +3535,11 @@ lkpi_sta_run_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int { int error; - error = lkpi_sta_run_to_init(vap, nstate, arg); + error = lkpi_sta_run_to_assoc(vap, nstate, arg); if (error != 0 && error != EALREADY) return (error); - /* At this point iv_bss is long a new node! */ - - error |= lkpi_sta_scan_to_auth(vap, nstate, 0); + error = lkpi_sta_assoc_to_auth(vap, nstate, arg); return (error); } @@ -3639,29 +3557,29 @@ struct fsm_state { int (*handler)(struct ieee80211vap *, enum ieee80211_state, int); } sta_state_fsm[] = { { IEEE80211_S_INIT, IEEE80211_S_INIT, lkpi_sta_state_do_nada }, - { IEEE80211_S_SCAN, IEEE80211_S_INIT, lkpi_sta_state_do_nada }, /* scan_to_init */ + { IEEE80211_S_SCAN, IEEE80211_S_INIT, lkpi_sta_state_do_nada }, /* DOWN4 scan_to_init */ { IEEE80211_S_AUTH, IEEE80211_S_INIT, lkpi_sta_auth_to_init }, /* not explicitly in sta_newstate() */ { IEEE80211_S_ASSOC, IEEE80211_S_INIT, lkpi_sta_assoc_to_init }, /* Send DEAUTH. */ { IEEE80211_S_RUN, IEEE80211_S_INIT, lkpi_sta_run_to_init }, /* Send DISASSOC. */ - { IEEE80211_S_INIT, IEEE80211_S_SCAN, lkpi_sta_state_do_nada }, + { IEEE80211_S_INIT, IEEE80211_S_SCAN, lkpi_sta_init_to_scan }, /* UP1 */ { IEEE80211_S_SCAN, IEEE80211_S_SCAN, lkpi_sta_state_do_nada }, - { IEEE80211_S_AUTH, IEEE80211_S_SCAN, lkpi_sta_auth_to_scan }, + { IEEE80211_S_AUTH, IEEE80211_S_SCAN, lkpi_sta_auth_to_scan }, /* DOWN3 */ { IEEE80211_S_ASSOC, IEEE80211_S_SCAN, lkpi_sta_assoc_to_scan }, { IEEE80211_S_RUN, IEEE80211_S_SCAN, lkpi_sta_run_to_scan }, /* Beacon miss. */ { IEEE80211_S_INIT, IEEE80211_S_AUTH, lkpi_sta_scan_to_auth }, /* Send AUTH. */ - { IEEE80211_S_SCAN, IEEE80211_S_AUTH, lkpi_sta_scan_to_auth }, /* Send AUTH. */ + { IEEE80211_S_SCAN, IEEE80211_S_AUTH, lkpi_sta_scan_to_auth }, /* UP2 Send AUTH. */ { IEEE80211_S_AUTH, IEEE80211_S_AUTH, lkpi_sta_a_to_a }, /* Send ?AUTH. */ - { IEEE80211_S_ASSOC, IEEE80211_S_AUTH, lkpi_sta_assoc_to_auth }, /* Send ?AUTH. */ + { IEEE80211_S_ASSOC, IEEE80211_S_AUTH, lkpi_sta_assoc_to_auth }, /* DOWN2 Send ?AUTH. */ { IEEE80211_S_RUN, IEEE80211_S_AUTH, lkpi_sta_run_to_auth }, /* Send ?AUTH. */ - { IEEE80211_S_AUTH, IEEE80211_S_ASSOC, lkpi_sta_auth_to_assoc }, /* Send ASSOCREQ. */ + { IEEE80211_S_AUTH, IEEE80211_S_ASSOC, lkpi_sta_auth_to_assoc }, /* UP3.1 Send ASSOCREQ. */ { IEEE80211_S_ASSOC, IEEE80211_S_ASSOC, lkpi_sta_a_to_a }, /* Send ASSOCREQ. */ - { IEEE80211_S_RUN, IEEE80211_S_ASSOC, lkpi_sta_run_to_assoc }, /* Send ASSOCREQ/REASSOCREQ. */ + { IEEE80211_S_RUN, IEEE80211_S_ASSOC, lkpi_sta_run_to_assoc }, /* DOWN1 Send ASSOCREQ/REASSOCREQ. */ - { IEEE80211_S_AUTH, IEEE80211_S_RUN, lkpi_sta_auth_to_run }, - { IEEE80211_S_ASSOC, IEEE80211_S_RUN, lkpi_sta_assoc_to_run }, + { IEEE80211_S_AUTH, IEEE80211_S_RUN, lkpi_sta_auth_to_run }, /* UP3.2 */ + { IEEE80211_S_ASSOC, IEEE80211_S_RUN, lkpi_sta_assoc_to_run }, /* UP4 */ { IEEE80211_S_RUN, IEEE80211_S_RUN, lkpi_sta_state_do_nada }, /* Dummy at the end without handler. */ @@ -3703,7 +3621,7 @@ lkpi_iv_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) } else { ic_printf(vap->iv_ic, "%s: only station mode currently supported: " - "cap %p iv_opmode %d\n", __func__, vap, vap->iv_opmode); + "vap %p iv_opmode %d\n", __func__, vap, vap->iv_opmode); return (ENOSYS); } @@ -3797,7 +3715,7 @@ lkpi_wme_update(struct lkpi_hw *lhw, struct ieee80211vap *vap, bool planned) struct chanAccParams chp; struct wmeParams wmeparr[WME_NUM_AC]; struct ieee80211_tx_queue_params txqp; - enum ieee80211_bss_changed changed; + enum ieee80211_bss_changed bss_changed; int error; uint16_t ac; @@ -3848,11 +3766,11 @@ lkpi_wme_update(struct lkpi_hw *lhw, struct ieee80211vap *vap, bool planned) ic_printf(ic, "%s: conf_tx ac %u failed %d\n", __func__, ac, error); } - changed = BSS_CHANGED_QOS; + bss_changed = BSS_CHANGED_QOS; if (!planned) - lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed); + lkpi_bss_info_change(hw, vif, bss_changed); - return (changed); + return (bss_changed); } #endif @@ -3918,7 +3836,7 @@ lkpi_iv_sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, * locking, see if queue_work() is fast enough. */ bss_changed = lkpi_update_dtim_tsf(vif, ni, ni->ni_vap, __func__, __LINE__); - lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed); + lkpi_bss_info_change(hw, vif, bss_changed); } /* @@ -3964,7 +3882,7 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], struct ieee80211vap *vap; struct ieee80211_vif *vif; struct ieee80211_tx_queue_params txqp; - enum ieee80211_bss_changed changed; + enum ieee80211_bss_changed bss_changed; struct sysctl_oid *node; size_t len; int error, i; @@ -4081,8 +3999,8 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], LKPI_80211_LHW_LVIF_UNLOCK(lhw); /* Set bss_info. */ - changed = 0; - lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed); + bss_changed = 0; + lkpi_bss_info_change(hw, vif, bss_changed); /* Configure tx queues (conf_tx), default WME & send BSS_CHANGED_QOS. */ IMPROVE("Hardcoded values; to fix see 802.11-2016, 9.4.2.29 EDCA Parameter Set element"); @@ -4100,8 +4018,8 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], __func__, ac, error); } wiphy_unlock(hw->wiphy); - changed = BSS_CHANGED_QOS; - lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed); + bss_changed = BSS_CHANGED_QOS; + lkpi_bss_info_change(hw, vif, bss_changed); /* Force MC init. */ lkpi_update_mcast_filter(ic); @@ -4173,6 +4091,11 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], SYSCTL_CHILDREN(node), OID_AUTO, "dump_stas", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, lvif, 0, lkpi_80211_dump_stas, "A", "Dump sta statistics of this vif"); + SYSCTL_ADD_PROC(&lvif->sysctl_ctx, + SYSCTL_CHILDREN(node), OID_AUTO, "dump_stas_queues", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE | CTLFLAG_SKIP, lvif, 0, + lkpi_80211_dump_sta_queues, "A", + "Dump queue statistics for any sta of this vif"); IMPROVE(); @@ -5513,6 +5436,8 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m) #endif ni = lsta->ni; + ieee80211_output_seqno_assign(ni, -1, m); + k = NULL; keyix = IEEE80211_KEYIX_NONE; wh = mtod(m, struct ieee80211_frame *); @@ -5641,8 +5566,10 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m) c = ic->ic_curchan; info->band = lkpi_net80211_chan_to_nl80211_band(c); info->hw_queue = vif->hw_queue[ac]; - if (m->m_flags & M_EAPOL) + if ((m->m_flags & M_EAPOL) != 0) { info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO; + info->flags |= IEEE80211_TX_CTL_USE_MINRATE; /* mt76 */ + } info->control.vif = vif; /* XXX-BZ info->control.rates */ #ifdef __notyet__ @@ -5667,6 +5594,8 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m) dev_kfree_skb(skb); return; } + /* Reset header as data might have moved. */ + hdr = (void *)skb->data; } #endif @@ -5690,6 +5619,7 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m) LKPI_80211_LTXQ_LOCK(ltxq); skb_queue_tail(<xq->skbq, skb); + ltxq->frms_enqueued++; #ifdef LINUXKPI_DEBUG_80211 if (linuxkpi_debug_80211 & D80211_TRACE_TX) printf("%s:%d mo_wake_tx_queue :: %d %lu lsta %p sta %p " @@ -5703,7 +5633,7 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m) #endif LKPI_80211_LTXQ_UNLOCK(ltxq); wiphy_lock(hw->wiphy); - lkpi_80211_mo_wake_tx_queue(hw, <xq->txq); + lkpi_80211_mo_wake_tx_queue(hw, <xq->txq, true); wiphy_unlock(hw->wiphy); return; @@ -5719,6 +5649,7 @@ ops_tx: control.sta = sta; wiphy_lock(hw->wiphy); lkpi_80211_mo_tx(hw, &control, skb); + lsta->frms_tx++; wiphy_unlock(hw->wiphy); } @@ -6912,7 +6843,9 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw) } if (bootverbose) { - ic_printf(ic, "netdev_features %b\n", hw->netdev_features, NETIF_F_BITS); + if (hw->netdev_features != 0) + ic_printf(ic, "netdev_features %b\n", + hw->netdev_features, NETIF_F_BITS); ieee80211_announce(ic); } @@ -7008,6 +6941,14 @@ lkpi_ieee80211_iterate_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *, struct ieee80211_key_conf *, void *), void *arg) { +#ifdef LINUXKPI_DEBUG_80211 + if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO) + net80211_vap_printf(LVIF_TO_VAP(VIF_TO_LVIF(vif)), + "%s:%d: lsta %6D added_to_drv %d kc[keyix %u] %p\n", + __func__, __LINE__, LSTA_TO_STA(lsta)->addr, ":", + lsta->added_to_drv, keyix, lsta->kc[keyix]); +#endif + if (!lsta->added_to_drv) return; @@ -7961,6 +7902,77 @@ linuxkpi_wiphy_free(struct wiphy *wiphy) kfree(lwiphy); } +static void +lkpi_wiphy_band_annotate(struct wiphy *wiphy) +{ + int band; + + for (band = 0; band < NUM_NL80211_BANDS; band++) { + struct ieee80211_supported_band *supband; + int i; + + supband = wiphy->bands[band]; + if (supband == NULL) + continue; + + switch (band) { + case NL80211_BAND_2GHZ: + case NL80211_BAND_5GHZ: + break; + default: +#ifdef LINUXKPI_DEBUG_80211 + IMPROVE("band %d(%s) not yet supported", + band, lkpi_nl80211_band_name(band)); + /* For bands added here, also check lkpi_lsta_alloc(). */ +#endif + continue; + } + + for (i = 0; i < supband->n_bitrates; i++) { + switch (band) { + case NL80211_BAND_2GHZ: + switch (supband->bitrates[i].bitrate) { + case 110: + case 55: + case 20: + case 10: + supband->bitrates[i].flags |= + IEEE80211_RATE_MANDATORY_B; + /* FALLTHROUGH */ + /* 11g only */ + case 240: + case 120: + case 60: + supband->bitrates[i].flags |= + IEEE80211_RATE_MANDATORY_G; + break; + } + break; + case NL80211_BAND_5GHZ: + switch (supband->bitrates[i].bitrate) { + case 240: + case 120: + case 60: + supband->bitrates[i].flags |= + IEEE80211_RATE_MANDATORY_A; + break; + } + break; + } + } + } +} + +int +linuxkpi_80211_wiphy_register(struct wiphy *wiphy) +{ + TODO("Lots of checks and initialization"); + + lkpi_wiphy_band_annotate(wiphy); + + return (0); +} + static uint32_t lkpi_cfg80211_calculate_bitrate_ht(struct rate_info *rate) { @@ -8121,6 +8133,8 @@ linuxkpi_ieee80211_tx_dequeue(struct ieee80211_hw *hw, LKPI_80211_LTXQ_LOCK(ltxq); skb = skb_dequeue(<xq->skbq); + if (skb != NULL) + ltxq->frms_dequeued++; LKPI_80211_LTXQ_UNLOCK(ltxq); stopped: @@ -8167,6 +8181,9 @@ _lkpi_ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_node *ni; struct mbuf *m; + if (skb == NULL) + return; + m = skb->m; skb->m = NULL; @@ -8192,13 +8209,13 @@ linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *hw, struct ieee80211_tx_status *txstat) { struct sk_buff *skb; - struct ieee80211_tx_info *info; + struct ieee80211_tx_info *info, _info = { }; struct ieee80211_ratectl_tx_status txs; struct ieee80211_node *ni; int status; skb = txstat->skb; - if (skb->m != NULL) { + if (skb != NULL && skb->m != NULL) { struct mbuf *m; m = skb->m; @@ -8208,7 +8225,13 @@ linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *hw, ni = NULL; } + /* + * If we have no info information on tx, set info to an all-zero struct + * to make the code (and debug output) simpler. + */ info = txstat->info; + if (info == NULL) + info = &_info; if (info->flags & IEEE80211_TX_STAT_ACK) { status = 0; /* No error. */ txs.status = IEEE80211_RATECTL_TX_SUCCESS; @@ -8273,7 +8296,8 @@ linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *hw, if (txstat->free_list) { _lkpi_ieee80211_free_txskb(hw, skb, status); - list_add_tail(&skb->list, txstat->free_list); + if (skb != NULL) + list_add_tail(&skb->list, txstat->free_list); } else { linuxkpi_ieee80211_free_txskb(hw, skb, status); } @@ -8614,7 +8638,7 @@ lkpi_ieee80211_wake_queues(struct ieee80211_hw *hw, int hwq) ltxq->stopped = false; if (!skb_queue_empty(<xq->skbq)) - lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]); + lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid], false); } } rcu_read_unlock(); @@ -8663,6 +8687,43 @@ linuxkpi_ieee80211_wake_queue(struct ieee80211_hw *hw, int qnum) spin_unlock_irqrestore(&lhw->txq_lock, flags); } +void +linuxkpi_ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + + LKPI_80211_LHW_TXQ_LOCK(lhw); + ieee80211_txq_schedule_start(hw, txq->ac); + do { + struct lkpi_txq *ltxq; + struct ieee80211_txq *ntxq; + struct ieee80211_tx_control control; + struct sk_buff *skb; + + ntxq = ieee80211_next_txq(hw, txq->ac); + if (ntxq == NULL) + break; + ltxq = TXQ_TO_LTXQ(ntxq); + + memset(&control, 0, sizeof(control)); + control.sta = ntxq->sta; + do { + skb = linuxkpi_ieee80211_tx_dequeue(hw, ntxq); + if (skb == NULL) + break; + ltxq->frms_tx++; + lkpi_80211_mo_tx(hw, &control, skb); + } while(1); + + ieee80211_return_txq(hw, ntxq, false); + } while (1); + ieee80211_txq_schedule_end(hw, txq->ac); + LKPI_80211_LHW_TXQ_UNLOCK(lhw); +} + /* -------------------------------------------------------------------------- */ /* This is just hardware queues. */ @@ -8764,41 +8825,6 @@ out: /* -------------------------------------------------------------------------- */ -void -linuxkpi_ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw, - struct ieee80211_txq *txq) -{ - struct lkpi_hw *lhw; - struct ieee80211_txq *ntxq; - struct ieee80211_tx_control control; - struct sk_buff *skb; - - lhw = HW_TO_LHW(hw); - - LKPI_80211_LHW_TXQ_LOCK(lhw); - ieee80211_txq_schedule_start(hw, txq->ac); - do { - ntxq = ieee80211_next_txq(hw, txq->ac); - if (ntxq == NULL) - break; - - memset(&control, 0, sizeof(control)); - control.sta = ntxq->sta; - do { - skb = linuxkpi_ieee80211_tx_dequeue(hw, ntxq); - if (skb == NULL) - break; - lkpi_80211_mo_tx(hw, &control, skb); - } while(1); - - ieee80211_return_txq(hw, ntxq, false); - } while (1); - ieee80211_txq_schedule_end(hw, txq->ac); - LKPI_80211_LHW_TXQ_UNLOCK(lhw); -} - -/* -------------------------------------------------------------------------- */ - struct lkpi_cfg80211_bss { u_int refcnt; struct cfg80211_bss bss; diff --git a/sys/compat/linuxkpi/common/src/linux_80211.h b/sys/compat/linuxkpi/common/src/linux_80211.h index fcbef46fc6de..8ae5c3d13d2d 100644 --- a/sys/compat/linuxkpi/common/src/linux_80211.h +++ b/sys/compat/linuxkpi/common/src/linux_80211.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2020-2023 The FreeBSD Foundation + * Copyright (c) 2020-2026 The FreeBSD Foundation * Copyright (c) 2020-2021 Bjoern A. Zeeb * * This software was developed by Björn Zeeb under sponsorship from @@ -154,6 +154,9 @@ struct lkpi_txq { bool stopped; uint32_t txq_generation; struct sk_buff_head skbq; + uint64_t frms_enqueued; + uint64_t frms_dequeued; + uint64_t frms_tx; /* Must be last! */ struct ieee80211_txq txq __aligned(CACHE_LINE_SIZE); @@ -180,6 +183,7 @@ struct lkpi_sta { bool in_mgd; /* XXX-BZ should this be per-vif? */ struct station_info sinfo; /* statistics */ + uint64_t frms_tx; /* (*tx) */ /* Must be last! */ struct ieee80211_sta sta __aligned(CACHE_LINE_SIZE); @@ -465,6 +469,10 @@ void lkpi_80211_mo_change_chanctx(struct ieee80211_hw *, struct ieee80211_chanctx_conf *, uint32_t); void lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *, struct ieee80211_chanctx_conf *); +void lkpi_80211_mo_vif_cfg_changed(struct ieee80211_hw *, struct ieee80211_vif *, + uint64_t, bool); +void lkpi_80211_mo_link_info_changed(struct ieee80211_hw *, struct ieee80211_vif *, + struct ieee80211_bss_conf *, uint64_t, uint8_t, bool); void lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_bss_conf *, uint64_t); int lkpi_80211_mo_conf_tx(struct ieee80211_hw *, struct ieee80211_vif *, @@ -477,7 +485,8 @@ void lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *, struct ieee80211_vif * struct ieee80211_prep_tx_info *); void lkpi_80211_mo_tx(struct ieee80211_hw *, struct ieee80211_tx_control *, struct sk_buff *); -void lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *, struct ieee80211_txq *); +void lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *, struct ieee80211_txq *, + bool); void lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *); void lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *); diff --git a/sys/compat/linuxkpi/common/src/linux_80211_macops.c b/sys/compat/linuxkpi/common/src/linux_80211_macops.c index a85e6d0d0dd7..42067e36c953 100644 --- a/sys/compat/linuxkpi/common/src/linux_80211_macops.c +++ b/sys/compat/linuxkpi/common/src/linux_80211_macops.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2021-2022 The FreeBSD Foundation + * Copyright (c) 2021-2026 The FreeBSD Foundation * * This software was developed by Björn Zeeb under sponsorship from * the FreeBSD Foundation. @@ -546,24 +546,80 @@ lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw, } void -lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_bss_conf *conf, uint64_t changed) +lkpi_80211_mo_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + uint64_t vif_cfg_bits, bool fallback) { struct lkpi_hw *lhw; + might_sleep(); + /* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */ + + lhw = HW_TO_LHW(hw); + if (lhw->ops->vif_cfg_changed == NULL && + lhw->ops->bss_info_changed == NULL) + return; + + if (vif_cfg_bits == 0) + return; + + LKPI_80211_TRACE_MO("hw %p vif %p vif_cfg_bits %#jx", hw, vif, (uintmax_t)vif_cfg_bits); + if (lhw->ops->link_info_changed != NULL) + lhw->ops->vif_cfg_changed(hw, vif, vif_cfg_bits); + else if (fallback) + lhw->ops->bss_info_changed(hw, vif, &vif->bss_conf, vif_cfg_bits); +} + +void +lkpi_80211_mo_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *conf, uint64_t link_info_bits, uint8_t link_id, + bool fallback) +{ + struct lkpi_hw *lhw; + + might_sleep(); + /* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */ + lhw = HW_TO_LHW(hw); if (lhw->ops->link_info_changed == NULL && lhw->ops->bss_info_changed == NULL) return; - if (changed == 0) + if (link_info_bits == 0) return; - LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)changed); + if (!ieee80211_vif_link_active(vif, link_id)) + return; + + LKPI_80211_TRACE_MO("hw %p vif %p conf %p link_info_bits %#jx", hw, vif, conf, (uintmax_t)link_info_bits); if (lhw->ops->link_info_changed != NULL) - lhw->ops->link_info_changed(hw, vif, conf, changed); - else - lhw->ops->bss_info_changed(hw, vif, conf, changed); + lhw->ops->link_info_changed(hw, vif, conf, link_info_bits); + else if (fallback) + lhw->ops->bss_info_changed(hw, vif, conf, link_info_bits); +} + +/* + * This is basically obsolete but one caller. + * The functionality is now split between lkpi_80211_mo_link_info_changed() and + * lkpi_80211_mo_vif_cfg_changed(). Those functions have a flag whether to call + * the (*bss_info_changed) fallback or not. See lkpi_bss_info_change(). + */ +void +lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *conf, uint64_t bss_changed) +{ + struct lkpi_hw *lhw; + + /* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */ + + lhw = HW_TO_LHW(hw); + if (lhw->ops->bss_info_changed == NULL) + return; + + if (bss_changed == 0) + return; + + LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)bss_changed); + lhw->ops->bss_info_changed(hw, vif, conf, bss_changed); } int @@ -644,11 +700,17 @@ lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl, } void -lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq) +lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq, + bool schedule) { struct lkpi_hw *lhw; lhw = HW_TO_LHW(hw); + + /* Do the schedule before the check for wake_tx_queue supported! */ + if (schedule) + ieee80211_schedule_txq(hw, txq); + if (lhw->ops->wake_tx_queue == NULL) return; diff --git a/sys/compat/linuxkpi/common/src/linux_compat.c b/sys/compat/linuxkpi/common/src/linux_compat.c index 35430daf311d..8fc644241d79 100644 --- a/sys/compat/linuxkpi/common/src/linux_compat.c +++ b/sys/compat/linuxkpi/common/src/linux_compat.c @@ -1894,56 +1894,168 @@ void lkpi_hex_dump(int(*_fpf)(void *, const char *, ...), void *arg1, const char *level, const char *prefix_str, const int prefix_type, const int rowsize, const int groupsize, - const void *buf, size_t len, const bool ascii) + const void *buf, size_t len, const bool ascii, const bool trailing_newline) { typedef const struct { long long value; } __packed *print_64p_t; typedef const struct { uint32_t value; } __packed *print_32p_t; typedef const struct { uint16_t value; } __packed *print_16p_t; const void *buf_old = buf; - int row; + int row, linelen, ret; while (len > 0) { - if (level != NULL) - _fpf(arg1, "%s", level); - if (prefix_str != NULL) - _fpf(arg1, "%s ", prefix_str); + linelen = 0; + if (level != NULL) { + ret = _fpf(arg1, "%s", level); + if (ret < 0) + break; + linelen += ret; + } + if (prefix_str != NULL) { + ret = _fpf( + arg1, "%s%s", linelen ? " " : "", prefix_str); + if (ret < 0) + break; + linelen += ret; + } switch (prefix_type) { case DUMP_PREFIX_ADDRESS: - _fpf(arg1, "[%p] ", buf); + ret = _fpf( + arg1, "%s[%p]", linelen ? " " : "", buf); + if (ret < 0) + return; + linelen += ret; break; case DUMP_PREFIX_OFFSET: - _fpf(arg1, "[%#tx] ", ((const char *)buf - - (const char *)buf_old)); + ret = _fpf( + arg1, "%s[%#tx]", linelen ? " " : "", + ((const char *)buf - (const char *)buf_old)); + if (ret < 0) + return; + linelen += ret; break; default: break; } for (row = 0; row != rowsize; row++) { if (groupsize == 8 && len > 7) { - _fpf(arg1, "%016llx ", ((print_64p_t)buf)->value); + ret = _fpf( + arg1, "%s%016llx", linelen ? " " : "", + ((print_64p_t)buf)->value); + if (ret < 0) + return; + linelen += ret; buf = (const uint8_t *)buf + 8; len -= 8; } else if (groupsize == 4 && len > 3) { - _fpf(arg1, "%08x ", ((print_32p_t)buf)->value); + ret = _fpf( + arg1, "%s%08x", linelen ? " " : "", + ((print_32p_t)buf)->value); + if (ret < 0) + return; + linelen += ret; buf = (const uint8_t *)buf + 4; len -= 4; } else if (groupsize == 2 && len > 1) { - _fpf(arg1, "%04x ", ((print_16p_t)buf)->value); + ret = _fpf( + arg1, "%s%04x", linelen ? " " : "", + ((print_16p_t)buf)->value); + if (ret < 0) + return; + linelen += ret; buf = (const uint8_t *)buf + 2; len -= 2; } else if (len > 0) { - _fpf(arg1, "%02x ", *(const uint8_t *)buf); + ret = _fpf( + arg1, "%s%02x", linelen ? " " : "", + *(const uint8_t *)buf); + if (ret < 0) + return; + linelen += ret; buf = (const uint8_t *)buf + 1; len--; } else { break; } } - _fpf(arg1, "\n"); + if (len > 0 && trailing_newline) { + ret = _fpf(arg1, "\n"); + if (ret < 0) + break; + } } } +struct hdtb_context { + char *linebuf; + size_t linebuflen; + int written; +}; + +static int +hdtb_cb(void *arg, const char *format, ...) +{ + struct hdtb_context *context; + int written; + va_list args; + + context = arg; + + va_start(args, format); + written = vsnprintf( + context->linebuf, context->linebuflen, format, args); + va_end(args); + + if (written < 0) + return (written); + + /* + * Linux' hex_dump_to_buffer() function has the same behaviour as + * snprintf() basically. Therefore, it returns the number of bytes it + * would have written if the destination buffer was large enough. + * + * If the destination buffer was exhausted, lkpi_hex_dump() will + * continue to call this callback but it will only compute the bytes it + * would have written but write nothing to that buffer. + */ + context->written += written; + + if (written < context->linebuflen) { + context->linebuf += written; + context->linebuflen -= written; + } else { + context->linebuf += context->linebuflen; + context->linebuflen = 0; + } + + return (written); +} + +int +lkpi_hex_dump_to_buffer(const void *buf, size_t len, int rowsize, + int groupsize, char *linebuf, size_t linebuflen, bool ascii) +{ + int written; + struct hdtb_context context; + + context.linebuf = linebuf; + context.linebuflen = linebuflen; + context.written = 0; + + if (rowsize != 16 && rowsize != 32) + rowsize = 16; + + len = min(len, rowsize); + + lkpi_hex_dump( + hdtb_cb, &context, NULL, NULL, DUMP_PREFIX_NONE, + rowsize, groupsize, buf, len, ascii, false); + + written = context.written; + + return (written); +} + static void linux_timer_callback_wrapper(void *context) { diff --git a/sys/compat/linuxkpi/common/src/linux_current.c b/sys/compat/linuxkpi/common/src/linux_current.c index c342eb279caa..3bc5d31d211a 100644 --- a/sys/compat/linuxkpi/common/src/linux_current.c +++ b/sys/compat/linuxkpi/common/src/linux_current.c @@ -90,11 +90,8 @@ linux_alloc_current(struct thread *td, int flags) } ts = uma_zalloc(linux_current_zone, flags | M_ZERO); - if (ts == NULL) { - if ((flags & (M_WAITOK | M_NOWAIT)) == M_WAITOK) - panic("linux_alloc_current: failed to allocate task"); + if (ts == NULL) return (ENOMEM); - } mm = NULL; /* setup new task structure */ @@ -118,10 +115,7 @@ linux_alloc_current(struct thread *td, int flags) PROC_UNLOCK(proc); mm = uma_zalloc(linux_mm_zone, flags | M_ZERO); if (mm == NULL) { - if ((flags & (M_WAITOK | M_NOWAIT)) == M_WAITOK) - panic( - "linux_alloc_current: failed to allocate mm"); - uma_zfree(linux_current_zone, mm); + uma_zfree(linux_current_zone, ts); return (ENOMEM); } diff --git a/sys/compat/linuxkpi/common/src/linux_eventfd.c b/sys/compat/linuxkpi/common/src/linux_eventfd.c new file mode 100644 index 000000000000..126c6c54b9a5 --- /dev/null +++ b/sys/compat/linuxkpi/common/src/linux_eventfd.c @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 2025 The FreeBSD Foundation + * Copyright (c) 2025 Jean-Sébastien Pédron + * + * This software was developed by Jean-Sébastien Pédron 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. + */ + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/filedesc.h> + +#include <linux/eventfd.h> + +struct eventfd_ctx * +lkpi_eventfd_ctx_fdget(int fd) +{ + struct file *fp; + struct eventfd_ctx *ctx; + + /* Lookup file pointer by file descriptor index. */ + if (fget_unlocked(curthread, fd, &cap_no_rights, &fp) != 0) + return (ERR_PTR(-EBADF)); + + /* + * eventfd_get() bumps the refcount, so we can safely release the + * reference on the file itself afterwards. + */ + ctx = eventfd_get(fp); + fdrop(fp, curthread); + + if (ctx == NULL) + return (ERR_PTR(-EBADF)); + + return (ctx); +} + +void +lkpi_eventfd_ctx_put(struct eventfd_ctx *ctx) +{ + eventfd_put(ctx); +} diff --git a/sys/compat/linuxkpi/common/src/linux_firmware.c b/sys/compat/linuxkpi/common/src/linux_firmware.c index 12658df5ce83..0c6d855501ef 100644 --- a/sys/compat/linuxkpi/common/src/linux_firmware.c +++ b/sys/compat/linuxkpi/common/src/linux_firmware.c @@ -66,7 +66,8 @@ _linuxkpi_request_firmware(const char *fw_name, const struct linuxkpi_firmware * uint32_t flags; if (fw_name == NULL || fw == NULL || dev == NULL) { - *fw = NULL; + if (fw != NULL) + *fw = NULL; return (-EINVAL); } diff --git a/sys/compat/linuxkpi/common/src/linux_pci.c b/sys/compat/linuxkpi/common/src/linux_pci.c index df5255d8a6ca..477cb321ea9e 100644 --- a/sys/compat/linuxkpi/common/src/linux_pci.c +++ b/sys/compat/linuxkpi/common/src/linux_pci.c @@ -385,14 +385,16 @@ lkpifill_pci_dev(device_t dev, struct pci_dev *pdev) pdev->dev.bsddev = dev; pdev->dev.parent = &linux_root_device; pdev->dev.release = lkpi_pci_dev_release; - INIT_LIST_HEAD(&pdev->dev.irqents); if (pci_msi_count(dev) > 0) pdev->msi_desc = malloc(pci_msi_count(dev) * sizeof(*pdev->msi_desc), M_DEVBUF, M_WAITOK | M_ZERO); + TAILQ_INIT(&pdev->mmio); + spin_lock_init(&pdev->pcie_cap_lock); spin_lock_init(&pdev->dev.devres_lock); INIT_LIST_HEAD(&pdev->dev.devres_head); + INIT_LIST_HEAD(&pdev->dev.irqents); return (0); } @@ -613,9 +615,6 @@ linux_pci_attach_device(device_t dev, struct pci_driver *pdrv, if (error) goto out_dma_init; - TAILQ_INIT(&pdev->mmio); - spin_lock_init(&pdev->pcie_cap_lock); - spin_lock(&pci_lock); list_add(&pdev->links, &pci_devices); spin_unlock(&pci_lock); @@ -1720,9 +1719,26 @@ lkpi_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len, } LINUX_DMA_PCTRIE_REMOVE(&priv->ptree, dma_addr); - if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) - dma_sync_single_for_cpu(dev, dma_addr, len, direction); + if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) != 0) + goto skip_sync; + /* dma_sync_single_for_cpu() unrolled to avoid lock recursicn. */ + switch (direction) { + case DMA_BIDIRECTIONAL: + bus_dmamap_sync(obj->dmat, obj->dmamap, BUS_DMASYNC_POSTREAD); + bus_dmamap_sync(obj->dmat, obj->dmamap, BUS_DMASYNC_PREREAD); + break; + case DMA_TO_DEVICE: + bus_dmamap_sync(obj->dmat, obj->dmamap, BUS_DMASYNC_POSTWRITE); + break; + case DMA_FROM_DEVICE: + bus_dmamap_sync(obj->dmat, obj->dmamap, BUS_DMASYNC_POSTREAD); + break; + default: + break; + } + +skip_sync: bus_dmamap_unload(obj->dmat, obj->dmamap); bus_dmamap_destroy(obj->dmat, obj->dmamap); DMA_PRIV_UNLOCK(priv); @@ -1796,6 +1812,42 @@ lkpi_dmam_free_coherent(struct device *dev, void *p) dma_free_coherent(dev, dr->size, dr->mem, *dr->handle); } +static int +lkpi_dmam_coherent_match(struct device *dev, void *dr, void *mp) +{ + struct lkpi_devres_dmam_coherent *a, *b; + + a = dr; + b = mp; + + if (a->mem != b->mem) + return (0); + if (a->size != b->size || a->handle != b->handle) + dev_WARN(dev, "for mem %p: size %zu != %zu || handle %#jx != %#jx\n", + a->mem, a->size, b->size, + (uintmax_t)a->handle, (uintmax_t)b->handle); + return (1); +} + +void +linuxkpi_dmam_free_coherent(struct device *dev, size_t size, + void *addr, dma_addr_t dma_handle) +{ + struct lkpi_devres_dmam_coherent match = { + .size = size, + .handle = &dma_handle, + .mem = addr + }; + int error; + + error = devres_destroy(dev, lkpi_dmam_free_coherent, + lkpi_dmam_coherent_match, &match); + if (error != 0) + dev_WARN(dev, "devres_destroy returned %d, size %zu addr %p " + "dma_handle %#jx\n", error, size, addr, (uintmax_t)dma_handle); + dma_free_coherent(dev, size, addr, dma_handle); +} + void * linuxkpi_dmam_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag) diff --git a/sys/compat/linuxkpi/common/src/linux_radix.c b/sys/compat/linuxkpi/common/src/linux_radix.c index ee6b3a63c370..760f583f25c8 100644 --- a/sys/compat/linuxkpi/common/src/linux_radix.c +++ b/sys/compat/linuxkpi/common/src/linux_radix.c @@ -52,6 +52,81 @@ radix_pos(long id, int height) return (id >> (RADIX_TREE_MAP_SHIFT * height)) & RADIX_TREE_MAP_MASK; } +static inline int +root_tag_get(const struct radix_tree_root *root, unsigned tag) +{ + return (test_bit(tag, root->tags)); +} + +static inline void +root_tag_set(struct radix_tree_root *root, unsigned tag) +{ + set_bit(tag, root->tags); +} + +static inline void +root_tag_clear(struct radix_tree_root *root, unsigned tag) +{ + clear_bit(tag, root->tags); +} + +static inline int +tag_get(const struct radix_tree_node *node, unsigned int tag, int pos) +{ + return (test_bit(pos, node->tags[tag])); +} + +static inline void +tag_set(struct radix_tree_node *node, unsigned int tag, int pos) +{ + set_bit(pos, node->tags[tag]); +} + +static inline void +tag_clear(struct radix_tree_node *node, unsigned int tag, int pos) +{ + clear_bit(pos, node->tags[tag]); +} + +static inline bool +any_tag_set(const struct radix_tree_node *node, unsigned int tag) +{ + unsigned int pos; + + for (pos = 0; pos < RADIX_TREE_TAG_LONGS; pos++) + if (node->tags[tag][pos]) + return (true); + + return (false); +} + +static void +node_tag_clear(struct radix_tree_root *root, struct radix_tree_node *stack[], + unsigned long index, unsigned int tag) +{ + struct radix_tree_node *node; + int height, pos; + + height = 1; + node = stack[height]; + + while (node && height <= root->height - 1) { + pos = radix_pos(index, height); + if (!tag_get(node, tag, pos)) + return; + + tag_clear(node, tag, pos); + if (any_tag_set(node, tag)) + return; + + height++; + node = stack[height]; + } + + if (root_tag_get(root, tag)) + root_tag_clear(root, tag); +} + static void radix_tree_clean_root_node(struct radix_tree_root *root) { @@ -84,14 +159,70 @@ out: return (item); } +unsigned int +radix_tree_gang_lookup(const struct radix_tree_root *root, void **results, + unsigned long first_index, unsigned int max_items) +{ + struct radix_tree_iter iter; + void **slot; + int count; + + count = 0; + if (max_items == 0) + return (count); + + radix_tree_for_each_slot(slot, root, &iter, first_index) { + results[count] = *slot; + if (results[count] == NULL) + continue; + + count++; + if (count == max_items) + break; + } + + return (count); +} + +unsigned int +radix_tree_gang_lookup_tag(const struct radix_tree_root *root, + void **results, unsigned long first_index, unsigned int max_items, + unsigned int tag) +{ + struct radix_tree_iter iter; + void **slot; + int count; + + count = 0; + if (max_items == 0) + return (count); + + radix_tree_for_each_slot_tagged(slot, root, &iter, first_index, tag) { + results[count] = *slot; + if (results[count] == NULL) + continue; + + count++; + if (count == max_items) + break; + } + + return (count); +} + bool radix_tree_iter_find(const struct radix_tree_root *root, - struct radix_tree_iter *iter, void ***pppslot) + struct radix_tree_iter *iter, void ***pppslot, int flags) { struct radix_tree_node *node; unsigned long index = iter->index; + unsigned int tag; int height; + tag = flags & RADIX_TREE_ITER_TAG_MASK; + if ((flags & RADIX_TREE_ITER_TAGGED) && !root_tag_get(root, tag)) + return (false); + restart: node = root->rnode; if (node == NULL) @@ -109,7 +240,9 @@ restart: *pppslot = node->slots + pos; next = node->slots[pos]; - if (next == NULL) { + if (next == NULL || + (flags & RADIX_TREE_ITER_TAGGED && + !tag_get(next, tag, pos))) { index += step; index &= -step; if ((index & mask) == 0) @@ -131,6 +264,7 @@ radix_tree_delete(struct radix_tree_root *root, unsigned long index) void *item; int height; int idx; + int tag; item = NULL; node = root->rnode; @@ -144,9 +278,15 @@ radix_tree_delete(struct radix_tree_root *root, unsigned long index) stack[height] = node; node = node->slots[radix_pos(index, height--)]; } - idx = radix_pos(index, 0); - if (node) + + if (node) { + idx = radix_pos(index, 0); item = node->slots[idx]; + + for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) + node_tag_clear(root, stack, index, tag); + } + /* * If we removed something reduce the height of the tree. */ @@ -380,3 +520,74 @@ radix_tree_store(struct radix_tree_root *root, unsigned long index, void **ppite node->count++; return (0); } + +void * +radix_tree_tag_set(struct radix_tree_root *root, unsigned long index, + unsigned int tag) +{ + struct radix_tree_node *node; + void *item; + int height, pos; + + item = NULL; + node = root->rnode; + height = root->height - 1; + if (index > radix_max(root)) + goto out; + + while (height) { + KASSERT( + node != NULL, + ("Node at index %lu does not exist in radix tree %p", + index, root)); + + pos = radix_pos(index, height--); + node = node->slots[pos]; + + if (!tag_get(node, tag, pos)) + tag_set(node, tag, pos); + } + + item = node->slots[radix_pos(index, 0)]; + + root_tag_set(root, tag); + +out: + return (item); +} + +void * +radix_tree_tag_clear(struct radix_tree_root *root, + unsigned long index, unsigned int tag) +{ + struct radix_tree_node *stack[RADIX_TREE_MAX_HEIGHT]; + struct radix_tree_node *node; + void *item; + int height; + + item = NULL; + node = root->rnode; + height = root->height - 1; + if (index > radix_max(root)) + goto out; + + while (height && node) { + stack[height] = node; + node = node->slots[radix_pos(index, height--)]; + } + + if (node) { + item = node->slots[radix_pos(index, 0)]; + + node_tag_clear(root, stack, index, tag); + } + +out: + return (item); +} + +int +radix_tree_tagged(const struct radix_tree_root *root, unsigned int tag) +{ + return (root_tag_get(root, tag)); +} diff --git a/sys/compat/linuxkpi/common/src/linux_seq_buf.c b/sys/compat/linuxkpi/common/src/linux_seq_buf.c new file mode 100644 index 000000000000..112c53044c22 --- /dev/null +++ b/sys/compat/linuxkpi/common/src/linux_seq_buf.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025-2026 The FreeBSD Foundation + * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org> + * + * This software was developed by Jean-Sébastien Pédron under sponsorship + * from the FreeBSD Foundation. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <linux/seq_buf.h> + +void +linuxkpi_seq_buf_init(struct seq_buf *s, char *buf, unsigned int size) +{ + s->buffer = buf; + s->size = size; + + seq_buf_clear(s); +} + +int +linuxkpi_seq_buf_printf(struct seq_buf *s, const char *fmt, ...) +{ + int ret; + va_list args; + + va_start(args, fmt); + ret = seq_buf_vprintf(s, fmt, args); + va_end(args); + + return (ret); +} + +int +linuxkpi_seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args) +{ + int ret; + + if (!seq_buf_has_overflowed(s)) { + ret = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args); + if (s->len + ret < s->size) { + s->len += ret; + return (0); + } + } + + seq_buf_set_overflow(s); + return (-1); +} + +const char * +linuxkpi_seq_buf_str(struct seq_buf *s) +{ + if (s->size == 0) + return (""); + + if (seq_buf_buffer_left(s)) + s->buffer[s->len] = '\0'; + else + s->buffer[s->size - 1] = '\0'; + + return (s->buffer); +} diff --git a/sys/compat/linuxkpi/common/src/linux_seq_file.c b/sys/compat/linuxkpi/common/src/linux_seq_file.c index 9c06fe27bebe..eae414ea696e 100644 --- a/sys/compat/linuxkpi/common/src/linux_seq_file.c +++ b/sys/compat/linuxkpi/common/src/linux_seq_file.c @@ -40,7 +40,7 @@ MALLOC_DEFINE(M_LSEQ, "seq_file", "seq_file"); ssize_t -seq_read(struct linux_file *f, char *ubuf, size_t size, off_t *ppos) +seq_read(struct linux_file *f, char __user *ubuf, size_t size, off_t *ppos) { struct seq_file *m; struct sbuf *sbuf; @@ -49,6 +49,7 @@ seq_read(struct linux_file *f, char *ubuf, size_t size, off_t *ppos) m = f->private_data; sbuf = m->buf; + sbuf_clear(sbuf); p = m->op->start(m, ppos); rc = m->op->show(m, p); @@ -59,15 +60,8 @@ seq_read(struct linux_file *f, char *ubuf, size_t size, off_t *ppos) if (rc) return (rc); - rc = sbuf_len(sbuf); - if (*ppos >= rc || size < 1) - return (-EINVAL); - - size = min(rc - *ppos, size); - memcpy(ubuf, sbuf_data(sbuf) + *ppos, size); - *ppos += size; - - return (size); + return (simple_read_from_buffer(ubuf, size, ppos, sbuf_data(sbuf), + sbuf_len(sbuf))); } int diff --git a/sys/compat/linuxkpi/common/src/linux_shmemfs.c b/sys/compat/linuxkpi/common/src/linux_shmemfs.c index 1fb17bc5c0cb..d5c118ba7624 100644 --- a/sys/compat/linuxkpi/common/src/linux_shmemfs.c +++ b/sys/compat/linuxkpi/common/src/linux_shmemfs.c @@ -62,11 +62,10 @@ linux_shmem_read_mapping_page_gfp(vm_object_t obj, int pindex, gfp_t gfp) struct linux_file * linux_shmem_file_setup(const char *name, loff_t size, unsigned long flags) { - struct fileobj { + struct { struct linux_file file __aligned(sizeof(void *)); struct vnode vnode __aligned(sizeof(void *)); - }; - struct fileobj *fileobj; + } *fileobj; struct linux_file *filp; struct vnode *vp; int error; @@ -89,7 +88,7 @@ linux_shmem_file_setup(const char *name, loff_t size, unsigned long flags) } return (filp); err_1: - kfree(filp); + kfree(fileobj); err_0: return (ERR_PTR(error)); } diff --git a/sys/compat/linuxkpi/common/src/linux_simple_attr.c b/sys/compat/linuxkpi/common/src/linux_simple_attr.c index 8cc9ec7ecbc9..e5514194cb33 100644 --- a/sys/compat/linuxkpi/common/src/linux_simple_attr.c +++ b/sys/compat/linuxkpi/common/src/linux_simple_attr.c @@ -99,7 +99,8 @@ simple_attr_release(struct inode *inode, struct file *filp) * On failure, negative signed ERRNO */ ssize_t -simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t *ppos) +simple_attr_read(struct file *filp, char __user *buf, size_t read_size, + loff_t *ppos) { struct simple_attr *sattr; uint64_t data; @@ -119,18 +120,9 @@ simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t *ppos) scnprintf(prebuf, sizeof(prebuf), sattr->fmt, data); - ret = strlen(prebuf) + 1; - if (*ppos >= ret || read_size < 1) { - ret = -EINVAL; - goto unlock; - } - - read_size = min(ret - *ppos, read_size); - ret = strscpy(buf, prebuf + *ppos, read_size); - /* add 1 for null terminator */ - if (ret > 0) - ret += 1; + ret = simple_read_from_buffer(buf, read_size, ppos, prebuf, + strlen(prebuf) + 1); unlock: mutex_unlock(&sattr->mutex); @@ -155,29 +147,38 @@ unlock: * On failure, negative signed ERRNO */ static ssize_t -simple_attr_write_common(struct file *filp, const char *buf, size_t write_size, - loff_t *ppos, bool is_signed) +simple_attr_write_common(struct file *filp, const char __user *ubuf, + size_t write_size, loff_t *ppos, bool is_signed) { struct simple_attr *sattr; unsigned long long data; - size_t bufsize; + char *buf; ssize_t ret; sattr = filp->private_data; - bufsize = strlen(buf) + 1; if (sattr->set == NULL) return (-EFAULT); - if (*ppos >= bufsize || write_size < 1) + if (*ppos != 0 || write_size < 1) return (-EINVAL); + buf = malloc(write_size, M_LSATTR, M_WAITOK); + if (copy_from_user(buf, ubuf, write_size) != 0) { + free(buf, M_LSATTR); + return (-EFAULT); + } + if (strnlen(buf, write_size) == write_size) { + free(buf, M_LSATTR); + return (-EINVAL); + } + mutex_lock(&sattr->mutex); if (is_signed) - ret = kstrtoll(buf + *ppos, 0, &data); + ret = kstrtoll(buf, 0, &data); else - ret = kstrtoull(buf + *ppos, 0, &data); + ret = kstrtoull(buf, 0, &data); if (ret) goto unlock; @@ -185,23 +186,24 @@ simple_attr_write_common(struct file *filp, const char *buf, size_t write_size, if (ret) goto unlock; - ret = bufsize - *ppos; + ret = write_size; unlock: mutex_unlock(&sattr->mutex); + free(buf, M_LSATTR); return (ret); } ssize_t -simple_attr_write(struct file *filp, const char *buf, size_t write_size, +simple_attr_write(struct file *filp, const char __user *buf, size_t write_size, loff_t *ppos) { return (simple_attr_write_common(filp, buf, write_size, ppos, false)); } ssize_t -simple_attr_write_signed(struct file *filp, const char *buf, size_t write_size, - loff_t *ppos) +simple_attr_write_signed(struct file *filp, const char __user *buf, + size_t write_size, loff_t *ppos) { return (simple_attr_write_common(filp, buf, write_size, ppos, true)); } diff --git a/sys/compat/linuxkpi/common/src/linux_siphash.c b/sys/compat/linuxkpi/common/src/linux_siphash.c new file mode 100644 index 000000000000..b4842a8250e1 --- /dev/null +++ b/sys/compat/linuxkpi/common/src/linux_siphash.c @@ -0,0 +1,546 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +/* Copyright (C) 2016-2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. + * + * SipHash: a fast short-input PRF + * https://131002.net/siphash/ + * + * This implementation is specifically for SipHash2-4 for a secure PRF + * and HalfSipHash1-3/SipHash1-3 for an insecure PRF only suitable for + * hashtables. + */ + +#include <linux/siphash.h> +#include <asm/unaligned.h> + +#if defined(CONFIG_DCACHE_WORD_ACCESS) && BITS_PER_LONG == 64 +#include <linux/dcache.h> +#include <asm/word-at-a-time.h> +#endif + +#define EXPORT_SYMBOL(name) + +#define SIPROUND SIPHASH_PERMUTATION(v0, v1, v2, v3) + +#define PREAMBLE(len) \ + u64 v0 = SIPHASH_CONST_0; \ + u64 v1 = SIPHASH_CONST_1; \ + u64 v2 = SIPHASH_CONST_2; \ + u64 v3 = SIPHASH_CONST_3; \ + u64 b = ((u64)(len)) << 56; \ + v3 ^= key->key[1]; \ + v2 ^= key->key[0]; \ + v1 ^= key->key[1]; \ + v0 ^= key->key[0]; + +#define POSTAMBLE \ + v3 ^= b; \ + SIPROUND; \ + SIPROUND; \ + v0 ^= b; \ + v2 ^= 0xff; \ + SIPROUND; \ + SIPROUND; \ + SIPROUND; \ + SIPROUND; \ + return (v0 ^ v1) ^ (v2 ^ v3); + +#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS +u64 __siphash_aligned(const void *_data, size_t len, const siphash_key_t *key) +{ + const u8 *data = _data; + const u8 *end = data + len - (len % sizeof(u64)); + const u8 left = len & (sizeof(u64) - 1); + u64 m; + PREAMBLE(len) + for (; data != end; data += sizeof(u64)) { + m = le64_to_cpup(data); + v3 ^= m; + SIPROUND; + SIPROUND; + v0 ^= m; + } +#if defined(CONFIG_DCACHE_WORD_ACCESS) && BITS_PER_LONG == 64 + if (left) + b |= le64_to_cpu((__force __le64)(load_unaligned_zeropad(data) & + bytemask_from_count(left))); +#else + switch (left) { + case 7: b |= ((u64)end[6]) << 48; fallthrough; + case 6: b |= ((u64)end[5]) << 40; fallthrough; + case 5: b |= ((u64)end[4]) << 32; fallthrough; + case 4: b |= le32_to_cpup(data); break; + case 3: b |= ((u64)end[2]) << 16; fallthrough; + case 2: b |= le16_to_cpup(data); break; + case 1: b |= end[0]; + } +#endif + POSTAMBLE +} +EXPORT_SYMBOL(__siphash_aligned); +#endif + +u64 __siphash_unaligned(const void *_data, size_t len, const siphash_key_t *key) +{ + const u8 *data = _data; + const u8 *end = data + len - (len % sizeof(u64)); + const u8 left = len & (sizeof(u64) - 1); + u64 m; + PREAMBLE(len) + for (; data != end; data += sizeof(u64)) { + m = get_unaligned_le64(data); + v3 ^= m; + SIPROUND; + SIPROUND; + v0 ^= m; + } +#if defined(CONFIG_DCACHE_WORD_ACCESS) && BITS_PER_LONG == 64 + if (left) + b |= le64_to_cpu((__force __le64)(load_unaligned_zeropad(data) & + bytemask_from_count(left))); +#else + switch (left) { + case 7: b |= ((u64)end[6]) << 48; fallthrough; + case 6: b |= ((u64)end[5]) << 40; fallthrough; + case 5: b |= ((u64)end[4]) << 32; fallthrough; + case 4: b |= get_unaligned_le32(end); break; + case 3: b |= ((u64)end[2]) << 16; fallthrough; + case 2: b |= get_unaligned_le16(end); break; + case 1: b |= end[0]; + } +#endif + POSTAMBLE +} +EXPORT_SYMBOL(__siphash_unaligned); + +/** + * siphash_1u64 - compute 64-bit siphash PRF value of a u64 + * @first: first u64 + * @key: the siphash key + */ +u64 siphash_1u64(const u64 first, const siphash_key_t *key) +{ + PREAMBLE(8) + v3 ^= first; + SIPROUND; + SIPROUND; + v0 ^= first; + POSTAMBLE +} +EXPORT_SYMBOL(siphash_1u64); + +/** + * siphash_2u64 - compute 64-bit siphash PRF value of 2 u64 + * @first: first u64 + * @second: second u64 + * @key: the siphash key + */ +u64 siphash_2u64(const u64 first, const u64 second, const siphash_key_t *key) +{ + PREAMBLE(16) + v3 ^= first; + SIPROUND; + SIPROUND; + v0 ^= first; + v3 ^= second; + SIPROUND; + SIPROUND; + v0 ^= second; + POSTAMBLE +} +EXPORT_SYMBOL(siphash_2u64); + +/** + * siphash_3u64 - compute 64-bit siphash PRF value of 3 u64 + * @first: first u64 + * @second: second u64 + * @third: third u64 + * @key: the siphash key + */ +u64 siphash_3u64(const u64 first, const u64 second, const u64 third, + const siphash_key_t *key) +{ + PREAMBLE(24) + v3 ^= first; + SIPROUND; + SIPROUND; + v0 ^= first; + v3 ^= second; + SIPROUND; + SIPROUND; + v0 ^= second; + v3 ^= third; + SIPROUND; + SIPROUND; + v0 ^= third; + POSTAMBLE +} +EXPORT_SYMBOL(siphash_3u64); + +/** + * siphash_4u64 - compute 64-bit siphash PRF value of 4 u64 + * @first: first u64 + * @second: second u64 + * @third: third u64 + * @forth: forth u64 + * @key: the siphash key + */ +u64 siphash_4u64(const u64 first, const u64 second, const u64 third, + const u64 forth, const siphash_key_t *key) +{ + PREAMBLE(32) + v3 ^= first; + SIPROUND; + SIPROUND; + v0 ^= first; + v3 ^= second; + SIPROUND; + SIPROUND; + v0 ^= second; + v3 ^= third; + SIPROUND; + SIPROUND; + v0 ^= third; + v3 ^= forth; + SIPROUND; + SIPROUND; + v0 ^= forth; + POSTAMBLE +} +EXPORT_SYMBOL(siphash_4u64); + +u64 siphash_1u32(const u32 first, const siphash_key_t *key) +{ + PREAMBLE(4) + b |= first; + POSTAMBLE +} +EXPORT_SYMBOL(siphash_1u32); + +u64 siphash_3u32(const u32 first, const u32 second, const u32 third, + const siphash_key_t *key) +{ + u64 combined = (u64)second << 32 | first; + PREAMBLE(12) + v3 ^= combined; + SIPROUND; + SIPROUND; + v0 ^= combined; + b |= third; + POSTAMBLE +} +EXPORT_SYMBOL(siphash_3u32); + +#if BITS_PER_LONG == 64 +/* Note that on 64-bit, we make HalfSipHash1-3 actually be SipHash1-3, for + * performance reasons. On 32-bit, below, we actually implement HalfSipHash1-3. + */ + +#define HSIPROUND SIPROUND +#define HPREAMBLE(len) PREAMBLE(len) +#define HPOSTAMBLE \ + v3 ^= b; \ + HSIPROUND; \ + v0 ^= b; \ + v2 ^= 0xff; \ + HSIPROUND; \ + HSIPROUND; \ + HSIPROUND; \ + return (v0 ^ v1) ^ (v2 ^ v3); + +#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS +u32 __hsiphash_aligned(const void *_data, size_t len, const hsiphash_key_t *key) +{ + const u8 *data = _data; + const u8 *end = data + len - (len % sizeof(u64)); + const u8 left = len & (sizeof(u64) - 1); + u64 m; + HPREAMBLE(len) + for (; data != end; data += sizeof(u64)) { + m = le64_to_cpup(data); + v3 ^= m; + HSIPROUND; + v0 ^= m; + } +#if defined(CONFIG_DCACHE_WORD_ACCESS) && BITS_PER_LONG == 64 + if (left) + b |= le64_to_cpu((__force __le64)(load_unaligned_zeropad(data) & + bytemask_from_count(left))); +#else + switch (left) { + case 7: b |= ((u64)end[6]) << 48; fallthrough; + case 6: b |= ((u64)end[5]) << 40; fallthrough; + case 5: b |= ((u64)end[4]) << 32; fallthrough; + case 4: b |= le32_to_cpup(data); break; + case 3: b |= ((u64)end[2]) << 16; fallthrough; + case 2: b |= le16_to_cpup(data); break; + case 1: b |= end[0]; + } +#endif + HPOSTAMBLE +} +EXPORT_SYMBOL(__hsiphash_aligned); +#endif + +u32 __hsiphash_unaligned(const void *_data, size_t len, + const hsiphash_key_t *key) +{ + const u8 *data = _data; + const u8 *end = data + len - (len % sizeof(u64)); + const u8 left = len & (sizeof(u64) - 1); + u64 m; + HPREAMBLE(len) + for (; data != end; data += sizeof(u64)) { + m = get_unaligned_le64(data); + v3 ^= m; + HSIPROUND; + v0 ^= m; + } +#if defined(CONFIG_DCACHE_WORD_ACCESS) && BITS_PER_LONG == 64 + if (left) + b |= le64_to_cpu((__force __le64)(load_unaligned_zeropad(data) & + bytemask_from_count(left))); +#else + switch (left) { + case 7: b |= ((u64)end[6]) << 48; fallthrough; + case 6: b |= ((u64)end[5]) << 40; fallthrough; + case 5: b |= ((u64)end[4]) << 32; fallthrough; + case 4: b |= get_unaligned_le32(end); break; + case 3: b |= ((u64)end[2]) << 16; fallthrough; + case 2: b |= get_unaligned_le16(end); break; + case 1: b |= end[0]; + } +#endif + HPOSTAMBLE +} +EXPORT_SYMBOL(__hsiphash_unaligned); + +/** + * hsiphash_1u32 - compute 64-bit hsiphash PRF value of a u32 + * @first: first u32 + * @key: the hsiphash key + */ +u32 hsiphash_1u32(const u32 first, const hsiphash_key_t *key) +{ + HPREAMBLE(4) + b |= first; + HPOSTAMBLE +} +EXPORT_SYMBOL(hsiphash_1u32); + +/** + * hsiphash_2u32 - compute 32-bit hsiphash PRF value of 2 u32 + * @first: first u32 + * @second: second u32 + * @key: the hsiphash key + */ +u32 hsiphash_2u32(const u32 first, const u32 second, const hsiphash_key_t *key) +{ + u64 combined = (u64)second << 32 | first; + HPREAMBLE(8) + v3 ^= combined; + HSIPROUND; + v0 ^= combined; + HPOSTAMBLE +} +EXPORT_SYMBOL(hsiphash_2u32); + +/** + * hsiphash_3u32 - compute 32-bit hsiphash PRF value of 3 u32 + * @first: first u32 + * @second: second u32 + * @third: third u32 + * @key: the hsiphash key + */ +u32 hsiphash_3u32(const u32 first, const u32 second, const u32 third, + const hsiphash_key_t *key) +{ + u64 combined = (u64)second << 32 | first; + HPREAMBLE(12) + v3 ^= combined; + HSIPROUND; + v0 ^= combined; + b |= third; + HPOSTAMBLE +} +EXPORT_SYMBOL(hsiphash_3u32); + +/** + * hsiphash_4u32 - compute 32-bit hsiphash PRF value of 4 u32 + * @first: first u32 + * @second: second u32 + * @third: third u32 + * @forth: forth u32 + * @key: the hsiphash key + */ +u32 hsiphash_4u32(const u32 first, const u32 second, const u32 third, + const u32 forth, const hsiphash_key_t *key) +{ + u64 combined = (u64)second << 32 | first; + HPREAMBLE(16) + v3 ^= combined; + HSIPROUND; + v0 ^= combined; + combined = (u64)forth << 32 | third; + v3 ^= combined; + HSIPROUND; + v0 ^= combined; + HPOSTAMBLE +} +EXPORT_SYMBOL(hsiphash_4u32); +#else +#define HSIPROUND HSIPHASH_PERMUTATION(v0, v1, v2, v3) + +#define HPREAMBLE(len) \ + u32 v0 = HSIPHASH_CONST_0; \ + u32 v1 = HSIPHASH_CONST_1; \ + u32 v2 = HSIPHASH_CONST_2; \ + u32 v3 = HSIPHASH_CONST_3; \ + u32 b = ((u32)(len)) << 24; \ + v3 ^= key->key[1]; \ + v2 ^= key->key[0]; \ + v1 ^= key->key[1]; \ + v0 ^= key->key[0]; + +#define HPOSTAMBLE \ + v3 ^= b; \ + HSIPROUND; \ + v0 ^= b; \ + v2 ^= 0xff; \ + HSIPROUND; \ + HSIPROUND; \ + HSIPROUND; \ + return v1 ^ v3; + +#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS +u32 __hsiphash_aligned(const void *_data, size_t len, const hsiphash_key_t *key) +{ + const u8 *data = _data; + const u8 *end = data + len - (len % sizeof(u32)); + const u8 left = len & (sizeof(u32) - 1); + u32 m; + HPREAMBLE(len) + for (; data != end; data += sizeof(u32)) { + m = le32_to_cpup(data); + v3 ^= m; + HSIPROUND; + v0 ^= m; + } + switch (left) { + case 3: b |= ((u32)end[2]) << 16; fallthrough; + case 2: b |= le16_to_cpup(data); break; + case 1: b |= end[0]; + } + HPOSTAMBLE +} +EXPORT_SYMBOL(__hsiphash_aligned); +#endif + +u32 __hsiphash_unaligned(const void *_data, size_t len, + const hsiphash_key_t *key) +{ + const u8 *data = _data; + const u8 *end = data + len - (len % sizeof(u32)); + const u8 left = len & (sizeof(u32) - 1); + u32 m; + HPREAMBLE(len) + for (; data != end; data += sizeof(u32)) { + m = get_unaligned_le32(data); + v3 ^= m; + HSIPROUND; + v0 ^= m; + } + switch (left) { + case 3: b |= ((u32)end[2]) << 16; fallthrough; + case 2: b |= get_unaligned_le16(end); break; + case 1: b |= end[0]; + } + HPOSTAMBLE +} +EXPORT_SYMBOL(__hsiphash_unaligned); + +/** + * hsiphash_1u32 - compute 32-bit hsiphash PRF value of a u32 + * @first: first u32 + * @key: the hsiphash key + */ +u32 hsiphash_1u32(const u32 first, const hsiphash_key_t *key) +{ + HPREAMBLE(4) + v3 ^= first; + HSIPROUND; + v0 ^= first; + HPOSTAMBLE +} +EXPORT_SYMBOL(hsiphash_1u32); + +/** + * hsiphash_2u32 - compute 32-bit hsiphash PRF value of 2 u32 + * @first: first u32 + * @second: second u32 + * @key: the hsiphash key + */ +u32 hsiphash_2u32(const u32 first, const u32 second, const hsiphash_key_t *key) +{ + HPREAMBLE(8) + v3 ^= first; + HSIPROUND; + v0 ^= first; + v3 ^= second; + HSIPROUND; + v0 ^= second; + HPOSTAMBLE +} +EXPORT_SYMBOL(hsiphash_2u32); + +/** + * hsiphash_3u32 - compute 32-bit hsiphash PRF value of 3 u32 + * @first: first u32 + * @second: second u32 + * @third: third u32 + * @key: the hsiphash key + */ +u32 hsiphash_3u32(const u32 first, const u32 second, const u32 third, + const hsiphash_key_t *key) +{ + HPREAMBLE(12) + v3 ^= first; + HSIPROUND; + v0 ^= first; + v3 ^= second; + HSIPROUND; + v0 ^= second; + v3 ^= third; + HSIPROUND; + v0 ^= third; + HPOSTAMBLE +} +EXPORT_SYMBOL(hsiphash_3u32); + +/** + * hsiphash_4u32 - compute 32-bit hsiphash PRF value of 4 u32 + * @first: first u32 + * @second: second u32 + * @third: third u32 + * @forth: forth u32 + * @key: the hsiphash key + */ +u32 hsiphash_4u32(const u32 first, const u32 second, const u32 third, + const u32 forth, const hsiphash_key_t *key) +{ + HPREAMBLE(16) + v3 ^= first; + HSIPROUND; + v0 ^= first; + v3 ^= second; + HSIPROUND; + v0 ^= second; + v3 ^= third; + HSIPROUND; + v0 ^= third; + v3 ^= forth; + HSIPROUND; + v0 ^= forth; + HPOSTAMBLE +} +EXPORT_SYMBOL(hsiphash_4u32); +#endif diff --git a/sys/compat/linuxkpi/common/src/linux_xarray.c b/sys/compat/linuxkpi/common/src/linux_xarray.c index 3f07f6d7c59f..8caefbaf7e50 100644 --- a/sys/compat/linuxkpi/common/src/linux_xarray.c +++ b/sys/compat/linuxkpi/common/src/linux_xarray.c @@ -389,7 +389,7 @@ __xa_empty(struct xarray *xa) XA_ASSERT_LOCKED(xa); - return (!radix_tree_iter_find(&xa->xa_head, &iter, &temp)); + return (!radix_tree_iter_find(&xa->xa_head, &iter, &temp, 0)); } bool @@ -426,7 +426,7 @@ __xa_next(struct xarray *xa, unsigned long *pindex, bool not_first) return (NULL); } - found = radix_tree_iter_find(&xa->xa_head, &iter, &ppslot); + found = radix_tree_iter_find(&xa->xa_head, &iter, &ppslot, 0); if (likely(found)) { retval = *ppslot; if (retval == NULL_VALUE) diff --git a/sys/compat/linuxkpi/dummy/include/linux/eventfd.h b/sys/compat/linuxkpi/dummy/include/linux/fpu.h index e69de29bb2d1..e69de29bb2d1 100644 --- a/sys/compat/linuxkpi/dummy/include/linux/eventfd.h +++ b/sys/compat/linuxkpi/dummy/include/linux/fpu.h diff --git a/sys/compat/linuxkpi/dummy/include/media/cec-notifier.h b/sys/compat/linuxkpi/dummy/include/linux/kdebug.h index e69de29bb2d1..e69de29bb2d1 100644 --- a/sys/compat/linuxkpi/dummy/include/media/cec-notifier.h +++ b/sys/compat/linuxkpi/dummy/include/linux/kdebug.h diff --git a/sys/compat/linuxkpi/dummy/include/net/page_pool/helpers.h b/sys/compat/linuxkpi/dummy/include/net/page_pool/helpers.h deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/sys/compat/linuxkpi/dummy/include/net/page_pool/helpers.h +++ /dev/null |
